blob: 12882b5dcd9cf6dca05a1e0d4516a518d84067da [file] [log] [blame]
dpranke@chromium.org70049b72011-10-14 00:38:18 +00001#!/usr/bin/env python
cbentzel@chromium.org18fc5562012-01-13 13:27:44 +00002# Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botf3378c22008-08-24 00:55:55 +00003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
initial.commit94958cf2008-07-26 22:42:52 +00005
rtenneti@chromium.org922a8222011-08-16 03:30:45 +00006"""This is a simple HTTP/FTP/SYNC/TCP/UDP/ server used for testing Chrome.
initial.commit94958cf2008-07-26 22:42:52 +00007
8It supports several test URLs, as specified by the handlers in TestPageHandler.
cbentzel@chromium.org0787bc72010-11-11 20:31:31 +00009By default, it listens on an ephemeral port and sends the port number back to
10the originating process over a pipe. The originating process can specify an
11explicit port if necessary.
initial.commit94958cf2008-07-26 22:42:52 +000012It can use https if you specify the flag --https=CERT where CERT is the path
13to a pem file containing the certificate and private key that should be used.
initial.commit94958cf2008-07-26 22:42:52 +000014"""
15
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +000016import asyncore
initial.commit94958cf2008-07-26 22:42:52 +000017import base64
18import BaseHTTPServer
19import cgi
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +000020import errno
initial.commit94958cf2008-07-26 22:42:52 +000021import optparse
22import os
rtenneti@chromium.org922a8222011-08-16 03:30:45 +000023import random
initial.commit94958cf2008-07-26 22:42:52 +000024import re
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +000025import select
initial.commit94958cf2008-07-26 22:42:52 +000026import SocketServer
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +000027import socket
initial.commit94958cf2008-07-26 22:42:52 +000028import sys
cbentzel@chromium.org0787bc72010-11-11 20:31:31 +000029import struct
initial.commit94958cf2008-07-26 22:42:52 +000030import time
battre@chromium.orgd4479e12011-11-07 17:09:19 +000031import urllib
cbentzel@chromium.orge30b32d2010-11-06 17:33:56 +000032import urlparse
phajdan.jr@chromium.orgbf74e2b2010-08-17 20:07:11 +000033import warnings
ahendrickson@chromium.orgab17b6a2011-05-24 20:14:39 +000034import zlib
phajdan.jr@chromium.orgbf74e2b2010-08-17 20:07:11 +000035
36# Ignore deprecation warnings, they make our output more cluttered.
37warnings.filterwarnings("ignore", category=DeprecationWarning)
timurrrr@chromium.orgb9006f52010-04-30 14:50:58 +000038
rtenneti@chromium.org922a8222011-08-16 03:30:45 +000039import echo_message
timurrrr@chromium.orgb9006f52010-04-30 14:50:58 +000040import pyftpdlib.ftpserver
initial.commit94958cf2008-07-26 22:42:52 +000041import tlslite
42import tlslite.api
timurrrr@chromium.orgb9006f52010-04-30 14:50:58 +000043
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +000044try:
45 import hashlib
46 _new_md5 = hashlib.md5
47except ImportError:
48 import md5
49 _new_md5 = md5.new
50
dpranke@chromium.org70049b72011-10-14 00:38:18 +000051try:
52 import json
53except ImportError:
54 import simplejson as json
55
davidben@chromium.org06fcf202010-09-22 18:15:23 +000056if sys.platform == 'win32':
57 import msvcrt
58
maruel@chromium.org756cf982009-03-05 12:46:38 +000059SERVER_HTTP = 0
erikkay@google.comd5182ff2009-01-08 20:45:27 +000060SERVER_FTP = 1
akalin@chromium.org154bb132010-11-12 02:20:27 +000061SERVER_SYNC = 2
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +000062SERVER_TCP_ECHO = 3
63SERVER_UDP_ECHO = 4
initial.commit94958cf2008-07-26 22:42:52 +000064
akalin@chromium.orgf8479c62010-11-27 01:52:52 +000065# Using debug() seems to cause hangs on XP: see http://crbug.com/64515 .
initial.commit94958cf2008-07-26 22:42:52 +000066debug_output = sys.stderr
67def debug(str):
68 debug_output.write(str + "\n")
69 debug_output.flush()
70
agl@chromium.orgf9e66792011-12-12 22:22:19 +000071class RecordingSSLSessionCache(object):
72 """RecordingSSLSessionCache acts as a TLS session cache and maintains a log of
73 lookups and inserts in order to test session cache behaviours."""
74
75 def __init__(self):
76 self.log = []
77
78 def __getitem__(self, sessionID):
79 self.log.append(('lookup', sessionID))
80 raise KeyError()
81
82 def __setitem__(self, sessionID, session):
83 self.log.append(('insert', sessionID))
84
initial.commit94958cf2008-07-26 22:42:52 +000085class StoppableHTTPServer(BaseHTTPServer.HTTPServer):
86 """This is a specialization of of BaseHTTPServer to allow it
87 to be exited cleanly (by setting its "stop" member to True)."""
88
89 def serve_forever(self):
90 self.stop = False
tonyg@chromium.org75054202010-03-31 22:06:10 +000091 self.nonce_time = None
initial.commit94958cf2008-07-26 22:42:52 +000092 while not self.stop:
93 self.handle_request()
94 self.socket.close()
95
96class HTTPSServer(tlslite.api.TLSSocketServerMixIn, StoppableHTTPServer):
97 """This is a specialization of StoppableHTTPerver that add https support."""
98
davidben@chromium.org31282a12010-08-07 01:10:02 +000099 def __init__(self, server_address, request_hander_class, cert_path,
agl@chromium.orgf9e66792011-12-12 22:22:19 +0000100 ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers,
101 record_resume_info):
initial.commit94958cf2008-07-26 22:42:52 +0000102 s = open(cert_path).read()
103 x509 = tlslite.api.X509()
104 x509.parse(s)
105 self.cert_chain = tlslite.api.X509CertChain([x509])
106 s = open(cert_path).read()
107 self.private_key = tlslite.api.parsePEMKey(s, private=True)
davidben@chromium.org31282a12010-08-07 01:10:02 +0000108 self.ssl_client_auth = ssl_client_auth
rsleevi@chromium.orgb2ecdab2010-08-21 04:02:44 +0000109 self.ssl_client_cas = []
110 for ca_file in ssl_client_cas:
111 s = open(ca_file).read()
112 x509 = tlslite.api.X509()
113 x509.parse(s)
114 self.ssl_client_cas.append(x509.subject)
rsleevi@chromium.org2124c812010-10-28 11:57:36 +0000115 self.ssl_handshake_settings = tlslite.api.HandshakeSettings()
116 if ssl_bulk_ciphers is not None:
117 self.ssl_handshake_settings.cipherNames = ssl_bulk_ciphers
initial.commit94958cf2008-07-26 22:42:52 +0000118
agl@chromium.orgf9e66792011-12-12 22:22:19 +0000119 if record_resume_info:
120 # If record_resume_info is true then we'll replace the session cache with
121 # an object that records the lookups and inserts that it sees.
122 self.session_cache = RecordingSSLSessionCache()
123 else:
124 self.session_cache = tlslite.api.SessionCache()
initial.commit94958cf2008-07-26 22:42:52 +0000125 StoppableHTTPServer.__init__(self, server_address, request_hander_class)
126
127 def handshake(self, tlsConnection):
128 """Creates the SSL connection."""
129 try:
130 tlsConnection.handshakeServer(certChain=self.cert_chain,
131 privateKey=self.private_key,
davidben@chromium.org31282a12010-08-07 01:10:02 +0000132 sessionCache=self.session_cache,
rsleevi@chromium.orgb2ecdab2010-08-21 04:02:44 +0000133 reqCert=self.ssl_client_auth,
rsleevi@chromium.org2124c812010-10-28 11:57:36 +0000134 settings=self.ssl_handshake_settings,
rsleevi@chromium.orgb2ecdab2010-08-21 04:02:44 +0000135 reqCAs=self.ssl_client_cas)
initial.commit94958cf2008-07-26 22:42:52 +0000136 tlsConnection.ignoreAbruptClose = True
137 return True
phajdan.jr@chromium.orgbf74e2b2010-08-17 20:07:11 +0000138 except tlslite.api.TLSAbruptCloseError:
139 # Ignore abrupt close.
140 return True
initial.commit94958cf2008-07-26 22:42:52 +0000141 except tlslite.api.TLSError, error:
142 print "Handshake failure:", str(error)
143 return False
144
akalin@chromium.org154bb132010-11-12 02:20:27 +0000145
146class SyncHTTPServer(StoppableHTTPServer):
147 """An HTTP server that handles sync commands."""
148
149 def __init__(self, server_address, request_handler_class):
150 # We import here to avoid pulling in chromiumsync's dependencies
151 # unless strictly necessary.
152 import chromiumsync
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000153 import xmppserver
akalin@chromium.org154bb132010-11-12 02:20:27 +0000154 StoppableHTTPServer.__init__(self, server_address, request_handler_class)
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000155 self._sync_handler = chromiumsync.TestServer()
156 self._xmpp_socket_map = {}
157 self._xmpp_server = xmppserver.XmppServer(
158 self._xmpp_socket_map, ('localhost', 0))
159 self.xmpp_port = self._xmpp_server.getsockname()[1]
lipalani@chromium.org0f7e6092011-09-23 01:26:10 +0000160 self.authenticated = True
akalin@chromium.org154bb132010-11-12 02:20:27 +0000161
akalin@chromium.org0332ec72011-08-27 06:53:21 +0000162 def GetXmppServer(self):
163 return self._xmpp_server
164
akalin@chromium.org154bb132010-11-12 02:20:27 +0000165 def HandleCommand(self, query, raw_request):
166 return self._sync_handler.HandleCommand(query, raw_request)
167
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000168 def HandleRequestNoBlock(self):
169 """Handles a single request.
170
171 Copied from SocketServer._handle_request_noblock().
172 """
173 try:
174 request, client_address = self.get_request()
175 except socket.error:
176 return
177 if self.verify_request(request, client_address):
178 try:
179 self.process_request(request, client_address)
180 except:
181 self.handle_error(request, client_address)
182 self.close_request(request)
183
lipalani@chromium.org0f7e6092011-09-23 01:26:10 +0000184 def SetAuthenticated(self, auth_valid):
185 self.authenticated = auth_valid
186
187 def GetAuthenticated(self):
188 return self.authenticated
189
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000190 def serve_forever(self):
191 """This is a merge of asyncore.loop() and SocketServer.serve_forever().
192 """
193
akalin@chromium.org3c0f42f2010-12-20 21:23:44 +0000194 def HandleXmppSocket(fd, socket_map, handler):
195 """Runs the handler for the xmpp connection for fd.
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000196
197 Adapted from asyncore.read() et al.
198 """
akalin@chromium.org3c0f42f2010-12-20 21:23:44 +0000199 xmpp_connection = socket_map.get(fd)
200 # This could happen if a previous handler call caused fd to get
201 # removed from socket_map.
202 if xmpp_connection is None:
203 return
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000204 try:
akalin@chromium.org3c0f42f2010-12-20 21:23:44 +0000205 handler(xmpp_connection)
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000206 except (asyncore.ExitNow, KeyboardInterrupt, SystemExit):
207 raise
208 except:
akalin@chromium.org3c0f42f2010-12-20 21:23:44 +0000209 xmpp_connection.handle_error()
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000210
211 while True:
212 read_fds = [ self.fileno() ]
213 write_fds = []
214 exceptional_fds = []
215
216 for fd, xmpp_connection in self._xmpp_socket_map.items():
217 is_r = xmpp_connection.readable()
218 is_w = xmpp_connection.writable()
219 if is_r:
220 read_fds.append(fd)
221 if is_w:
222 write_fds.append(fd)
223 if is_r or is_w:
224 exceptional_fds.append(fd)
225
226 try:
227 read_fds, write_fds, exceptional_fds = (
228 select.select(read_fds, write_fds, exceptional_fds))
229 except select.error, err:
230 if err.args[0] != errno.EINTR:
231 raise
232 else:
233 continue
234
235 for fd in read_fds:
236 if fd == self.fileno():
237 self.HandleRequestNoBlock()
238 continue
akalin@chromium.org3c0f42f2010-12-20 21:23:44 +0000239 HandleXmppSocket(fd, self._xmpp_socket_map,
240 asyncore.dispatcher.handle_read_event)
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000241
242 for fd in write_fds:
akalin@chromium.org3c0f42f2010-12-20 21:23:44 +0000243 HandleXmppSocket(fd, self._xmpp_socket_map,
244 asyncore.dispatcher.handle_write_event)
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000245
246 for fd in exceptional_fds:
akalin@chromium.org3c0f42f2010-12-20 21:23:44 +0000247 HandleXmppSocket(fd, self._xmpp_socket_map,
248 asyncore.dispatcher.handle_expt_event)
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +0000249
akalin@chromium.org154bb132010-11-12 02:20:27 +0000250
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +0000251class TCPEchoServer(SocketServer.TCPServer):
252 """A TCP echo server that echoes back what it has received."""
253
254 def server_bind(self):
255 """Override server_bind to store the server name."""
256 SocketServer.TCPServer.server_bind(self)
257 host, port = self.socket.getsockname()[:2]
258 self.server_name = socket.getfqdn(host)
259 self.server_port = port
260
261 def serve_forever(self):
262 self.stop = False
263 self.nonce_time = None
264 while not self.stop:
265 self.handle_request()
266 self.socket.close()
267
268
269class UDPEchoServer(SocketServer.UDPServer):
270 """A UDP echo server that echoes back what it has received."""
271
272 def server_bind(self):
273 """Override server_bind to store the server name."""
274 SocketServer.UDPServer.server_bind(self)
275 host, port = self.socket.getsockname()[:2]
276 self.server_name = socket.getfqdn(host)
277 self.server_port = port
278
279 def serve_forever(self):
280 self.stop = False
281 self.nonce_time = None
282 while not self.stop:
283 self.handle_request()
284 self.socket.close()
285
286
akalin@chromium.org154bb132010-11-12 02:20:27 +0000287class BasePageHandler(BaseHTTPServer.BaseHTTPRequestHandler):
288
289 def __init__(self, request, client_address, socket_server,
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000290 connect_handlers, get_handlers, head_handlers, post_handlers,
291 put_handlers):
akalin@chromium.org154bb132010-11-12 02:20:27 +0000292 self._connect_handlers = connect_handlers
293 self._get_handlers = get_handlers
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000294 self._head_handlers = head_handlers
akalin@chromium.org154bb132010-11-12 02:20:27 +0000295 self._post_handlers = post_handlers
296 self._put_handlers = put_handlers
297 BaseHTTPServer.BaseHTTPRequestHandler.__init__(
298 self, request, client_address, socket_server)
299
300 def log_request(self, *args, **kwargs):
301 # Disable request logging to declutter test log output.
302 pass
303
304 def _ShouldHandleRequest(self, handler_name):
305 """Determines if the path can be handled by the handler.
306
307 We consider a handler valid if the path begins with the
308 handler name. It can optionally be followed by "?*", "/*".
309 """
310
311 pattern = re.compile('%s($|\?|/).*' % handler_name)
312 return pattern.match(self.path)
313
314 def do_CONNECT(self):
315 for handler in self._connect_handlers:
316 if handler():
317 return
318
319 def do_GET(self):
320 for handler in self._get_handlers:
321 if handler():
322 return
323
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000324 def do_HEAD(self):
325 for handler in self._head_handlers:
326 if handler():
327 return
328
akalin@chromium.org154bb132010-11-12 02:20:27 +0000329 def do_POST(self):
330 for handler in self._post_handlers:
331 if handler():
332 return
333
334 def do_PUT(self):
335 for handler in self._put_handlers:
336 if handler():
337 return
338
339
340class TestPageHandler(BasePageHandler):
initial.commit94958cf2008-07-26 22:42:52 +0000341
342 def __init__(self, request, client_address, socket_server):
akalin@chromium.org154bb132010-11-12 02:20:27 +0000343 connect_handlers = [
wtc@chromium.org743d77b2009-02-11 02:48:15 +0000344 self.RedirectConnectHandler,
wtc@chromium.orgb86c7f92009-02-14 01:45:08 +0000345 self.ServerAuthConnectHandler,
wtc@chromium.org743d77b2009-02-11 02:48:15 +0000346 self.DefaultConnectResponseHandler]
akalin@chromium.org154bb132010-11-12 02:20:27 +0000347 get_handlers = [
initial.commit94958cf2008-07-26 22:42:52 +0000348 self.NoCacheMaxAgeTimeHandler,
349 self.NoCacheTimeHandler,
350 self.CacheTimeHandler,
351 self.CacheExpiresHandler,
352 self.CacheProxyRevalidateHandler,
353 self.CachePrivateHandler,
354 self.CachePublicHandler,
355 self.CacheSMaxAgeHandler,
356 self.CacheMustRevalidateHandler,
357 self.CacheMustRevalidateMaxAgeHandler,
358 self.CacheNoStoreHandler,
359 self.CacheNoStoreMaxAgeHandler,
360 self.CacheNoTransformHandler,
361 self.DownloadHandler,
362 self.DownloadFinishHandler,
363 self.EchoHeader,
ananta@chromium.org56812d02011-04-07 17:52:05 +0000364 self.EchoHeaderCache,
ericroman@google.coma47622b2008-11-15 04:36:51 +0000365 self.EchoAllHandler,
ahendrickson@chromium.orgab17b6a2011-05-24 20:14:39 +0000366 self.ZipFileHandler,
initial.commit94958cf2008-07-26 22:42:52 +0000367 self.FileHandler,
levin@chromium.orgf7ee2e42009-08-26 02:33:46 +0000368 self.SetCookieHandler,
battre@chromium.orgd4479e12011-11-07 17:09:19 +0000369 self.SetHeaderHandler,
initial.commit94958cf2008-07-26 22:42:52 +0000370 self.AuthBasicHandler,
371 self.AuthDigestHandler,
372 self.SlowServerHandler,
vsevik@chromium.orgf0e997e2011-05-20 09:36:14 +0000373 self.ChunkedServerHandler,
initial.commit94958cf2008-07-26 22:42:52 +0000374 self.ContentTypeHandler,
creis@google.com2f4f6a42011-03-25 19:44:19 +0000375 self.NoContentHandler,
initial.commit94958cf2008-07-26 22:42:52 +0000376 self.ServerRedirectHandler,
377 self.ClientRedirectHandler,
tony@chromium.org03266982010-03-05 03:18:42 +0000378 self.MultipartHandler,
tony@chromium.org4cb88302011-09-27 22:13:49 +0000379 self.MultipartSlowHandler,
agl@chromium.orgf9e66792011-12-12 22:22:19 +0000380 self.GetSSLSessionCacheHandler,
simonjam@chromium.orgf9cf32f2012-02-13 23:56:14 +0000381 self.CloseSocketHandler,
initial.commit94958cf2008-07-26 22:42:52 +0000382 self.DefaultResponseHandler]
akalin@chromium.org154bb132010-11-12 02:20:27 +0000383 post_handlers = [
initial.commit94958cf2008-07-26 22:42:52 +0000384 self.EchoTitleHandler,
mnissler@chromium.org7c939802010-11-11 08:47:14 +0000385 self.EchoHandler,
cbentzel@chromium.org18fc5562012-01-13 13:27:44 +0000386 self.DeviceManagementHandler,
387 self.PostOnlyFileHandler] + get_handlers
akalin@chromium.org154bb132010-11-12 02:20:27 +0000388 put_handlers = [
ananta@chromium.org56d146f2010-01-11 19:03:01 +0000389 self.EchoTitleHandler,
akalin@chromium.org154bb132010-11-12 02:20:27 +0000390 self.EchoHandler] + get_handlers
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000391 head_handlers = [
392 self.FileHandler,
393 self.DefaultResponseHandler]
initial.commit94958cf2008-07-26 22:42:52 +0000394
maruel@google.come250a9b2009-03-10 17:39:46 +0000395 self._mime_types = {
rafaelw@chromium.orga4e76f82010-09-09 17:33:18 +0000396 'crx' : 'application/x-chrome-extension',
lzheng@chromium.org02f09022010-12-16 20:24:35 +0000397 'exe' : 'application/octet-stream',
maruel@google.come250a9b2009-03-10 17:39:46 +0000398 'gif': 'image/gif',
399 'jpeg' : 'image/jpeg',
finnur@chromium.org88e84c32009-10-02 17:59:55 +0000400 'jpg' : 'image/jpeg',
lzheng@chromium.org02f09022010-12-16 20:24:35 +0000401 'pdf' : 'application/pdf',
402 'xml' : 'text/xml'
maruel@google.come250a9b2009-03-10 17:39:46 +0000403 }
initial.commit94958cf2008-07-26 22:42:52 +0000404 self._default_mime_type = 'text/html'
405
akalin@chromium.org154bb132010-11-12 02:20:27 +0000406 BasePageHandler.__init__(self, request, client_address, socket_server,
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000407 connect_handlers, get_handlers, head_handlers,
408 post_handlers, put_handlers)
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000409
initial.commit94958cf2008-07-26 22:42:52 +0000410 def GetMIMETypeFromName(self, file_name):
411 """Returns the mime type for the specified file_name. So far it only looks
412 at the file extension."""
413
rafaelw@chromium.orga4e76f82010-09-09 17:33:18 +0000414 (shortname, extension) = os.path.splitext(file_name.split("?")[0])
initial.commit94958cf2008-07-26 22:42:52 +0000415 if len(extension) == 0:
416 # no extension.
417 return self._default_mime_type
418
ericroman@google.comc17ca532009-05-07 03:51:05 +0000419 # extension starts with a dot, so we need to remove it
420 return self._mime_types.get(extension[1:], self._default_mime_type)
initial.commit94958cf2008-07-26 22:42:52 +0000421
initial.commit94958cf2008-07-26 22:42:52 +0000422 def NoCacheMaxAgeTimeHandler(self):
423 """This request handler yields a page with the title set to the current
424 system time, and no caching requested."""
425
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000426 if not self._ShouldHandleRequest("/nocachetime/maxage"):
initial.commit94958cf2008-07-26 22:42:52 +0000427 return False
428
429 self.send_response(200)
430 self.send_header('Cache-Control', 'max-age=0')
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000431 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000432 self.end_headers()
433
maruel@google.come250a9b2009-03-10 17:39:46 +0000434 self.wfile.write('<html><head><title>%s</title></head></html>' %
435 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000436
437 return True
438
439 def NoCacheTimeHandler(self):
440 """This request handler yields a page with the title set to the current
441 system time, and no caching requested."""
442
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000443 if not self._ShouldHandleRequest("/nocachetime"):
initial.commit94958cf2008-07-26 22:42:52 +0000444 return False
445
446 self.send_response(200)
447 self.send_header('Cache-Control', 'no-cache')
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000448 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000449 self.end_headers()
450
maruel@google.come250a9b2009-03-10 17:39:46 +0000451 self.wfile.write('<html><head><title>%s</title></head></html>' %
452 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000453
454 return True
455
456 def CacheTimeHandler(self):
457 """This request handler yields a page with the title set to the current
458 system time, and allows caching for one minute."""
459
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000460 if not self._ShouldHandleRequest("/cachetime"):
initial.commit94958cf2008-07-26 22:42:52 +0000461 return False
462
463 self.send_response(200)
464 self.send_header('Cache-Control', 'max-age=60')
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000465 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000466 self.end_headers()
467
maruel@google.come250a9b2009-03-10 17:39:46 +0000468 self.wfile.write('<html><head><title>%s</title></head></html>' %
469 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000470
471 return True
472
473 def CacheExpiresHandler(self):
474 """This request handler yields a page with the title set to the current
475 system time, and set the page to expire on 1 Jan 2099."""
476
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000477 if not self._ShouldHandleRequest("/cache/expires"):
initial.commit94958cf2008-07-26 22:42:52 +0000478 return False
479
480 self.send_response(200)
481 self.send_header('Expires', 'Thu, 1 Jan 2099 00:00:00 GMT')
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000482 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000483 self.end_headers()
484
maruel@google.come250a9b2009-03-10 17:39:46 +0000485 self.wfile.write('<html><head><title>%s</title></head></html>' %
486 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000487
488 return True
489
490 def CacheProxyRevalidateHandler(self):
491 """This request handler yields a page with the title set to the current
492 system time, and allows caching for 60 seconds"""
493
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000494 if not self._ShouldHandleRequest("/cache/proxy-revalidate"):
initial.commit94958cf2008-07-26 22:42:52 +0000495 return False
496
497 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000498 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000499 self.send_header('Cache-Control', 'max-age=60, proxy-revalidate')
500 self.end_headers()
501
maruel@google.come250a9b2009-03-10 17:39:46 +0000502 self.wfile.write('<html><head><title>%s</title></head></html>' %
503 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000504
505 return True
506
507 def CachePrivateHandler(self):
508 """This request handler yields a page with the title set to the current
509 system time, and allows caching for 5 seconds."""
510
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000511 if not self._ShouldHandleRequest("/cache/private"):
initial.commit94958cf2008-07-26 22:42:52 +0000512 return False
513
514 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000515 self.send_header('Content-Type', 'text/html')
huanr@chromium.orgab5be752009-05-23 02:58:44 +0000516 self.send_header('Cache-Control', 'max-age=3, private')
initial.commit94958cf2008-07-26 22:42:52 +0000517 self.end_headers()
518
maruel@google.come250a9b2009-03-10 17:39:46 +0000519 self.wfile.write('<html><head><title>%s</title></head></html>' %
520 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000521
522 return True
523
524 def CachePublicHandler(self):
525 """This request handler yields a page with the title set to the current
526 system time, and allows caching for 5 seconds."""
527
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000528 if not self._ShouldHandleRequest("/cache/public"):
initial.commit94958cf2008-07-26 22:42:52 +0000529 return False
530
531 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000532 self.send_header('Content-Type', 'text/html')
huanr@chromium.orgab5be752009-05-23 02:58:44 +0000533 self.send_header('Cache-Control', 'max-age=3, public')
initial.commit94958cf2008-07-26 22:42:52 +0000534 self.end_headers()
535
maruel@google.come250a9b2009-03-10 17:39:46 +0000536 self.wfile.write('<html><head><title>%s</title></head></html>' %
537 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000538
539 return True
540
541 def CacheSMaxAgeHandler(self):
542 """This request handler yields a page with the title set to the current
543 system time, and does not allow for caching."""
544
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000545 if not self._ShouldHandleRequest("/cache/s-maxage"):
initial.commit94958cf2008-07-26 22:42:52 +0000546 return False
547
548 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000549 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000550 self.send_header('Cache-Control', 'public, s-maxage = 60, max-age = 0')
551 self.end_headers()
552
maruel@google.come250a9b2009-03-10 17:39:46 +0000553 self.wfile.write('<html><head><title>%s</title></head></html>' %
554 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000555
556 return True
557
558 def CacheMustRevalidateHandler(self):
559 """This request handler yields a page with the title set to the current
560 system time, and does not allow caching."""
561
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000562 if not self._ShouldHandleRequest("/cache/must-revalidate"):
initial.commit94958cf2008-07-26 22:42:52 +0000563 return False
564
565 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000566 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000567 self.send_header('Cache-Control', 'must-revalidate')
568 self.end_headers()
569
maruel@google.come250a9b2009-03-10 17:39:46 +0000570 self.wfile.write('<html><head><title>%s</title></head></html>' %
571 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000572
573 return True
574
575 def CacheMustRevalidateMaxAgeHandler(self):
576 """This request handler yields a page with the title set to the current
577 system time, and does not allow caching event though max-age of 60
578 seconds is specified."""
579
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000580 if not self._ShouldHandleRequest("/cache/must-revalidate/max-age"):
initial.commit94958cf2008-07-26 22:42:52 +0000581 return False
582
583 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000584 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000585 self.send_header('Cache-Control', 'max-age=60, must-revalidate')
586 self.end_headers()
587
maruel@google.come250a9b2009-03-10 17:39:46 +0000588 self.wfile.write('<html><head><title>%s</title></head></html>' %
589 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000590
591 return True
592
initial.commit94958cf2008-07-26 22:42:52 +0000593 def CacheNoStoreHandler(self):
594 """This request handler yields a page with the title set to the current
595 system time, and does not allow the page to be stored."""
596
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000597 if not self._ShouldHandleRequest("/cache/no-store"):
initial.commit94958cf2008-07-26 22:42:52 +0000598 return False
599
600 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000601 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000602 self.send_header('Cache-Control', 'no-store')
603 self.end_headers()
604
maruel@google.come250a9b2009-03-10 17:39:46 +0000605 self.wfile.write('<html><head><title>%s</title></head></html>' %
606 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000607
608 return True
609
610 def CacheNoStoreMaxAgeHandler(self):
611 """This request handler yields a page with the title set to the current
612 system time, and does not allow the page to be stored even though max-age
613 of 60 seconds is specified."""
614
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000615 if not self._ShouldHandleRequest("/cache/no-store/max-age"):
initial.commit94958cf2008-07-26 22:42:52 +0000616 return False
617
618 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000619 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000620 self.send_header('Cache-Control', 'max-age=60, no-store')
621 self.end_headers()
622
maruel@google.come250a9b2009-03-10 17:39:46 +0000623 self.wfile.write('<html><head><title>%s</title></head></html>' %
624 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000625
626 return True
627
628
629 def CacheNoTransformHandler(self):
630 """This request handler yields a page with the title set to the current
631 system time, and does not allow the content to transformed during
632 user-agent caching"""
633
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000634 if not self._ShouldHandleRequest("/cache/no-transform"):
initial.commit94958cf2008-07-26 22:42:52 +0000635 return False
636
637 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000638 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000639 self.send_header('Cache-Control', 'no-transform')
640 self.end_headers()
641
maruel@google.come250a9b2009-03-10 17:39:46 +0000642 self.wfile.write('<html><head><title>%s</title></head></html>' %
643 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000644
645 return True
646
647 def EchoHeader(self):
648 """This handler echoes back the value of a specific request header."""
ananta@chromium.org219b2062009-10-23 16:09:41 +0000649 return self.EchoHeaderHelper("/echoheader")
initial.commit94958cf2008-07-26 22:42:52 +0000650
ananta@chromium.org56812d02011-04-07 17:52:05 +0000651 """This function echoes back the value of a specific request header"""
652 """while allowing caching for 16 hours."""
653 def EchoHeaderCache(self):
654 return self.EchoHeaderHelper("/echoheadercache")
ananta@chromium.org219b2062009-10-23 16:09:41 +0000655
656 def EchoHeaderHelper(self, echo_header):
657 """This function echoes back the value of the request header passed in."""
658 if not self._ShouldHandleRequest(echo_header):
initial.commit94958cf2008-07-26 22:42:52 +0000659 return False
660
661 query_char = self.path.find('?')
662 if query_char != -1:
663 header_name = self.path[query_char+1:]
664
665 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000666 self.send_header('Content-Type', 'text/plain')
ananta@chromium.org56812d02011-04-07 17:52:05 +0000667 if echo_header == '/echoheadercache':
668 self.send_header('Cache-control', 'max-age=60000')
669 else:
670 self.send_header('Cache-control', 'no-cache')
initial.commit94958cf2008-07-26 22:42:52 +0000671 # insert a vary header to properly indicate that the cachability of this
672 # request is subject to value of the request header being echoed.
673 if len(header_name) > 0:
674 self.send_header('Vary', header_name)
675 self.end_headers()
676
677 if len(header_name) > 0:
678 self.wfile.write(self.headers.getheader(header_name))
679
680 return True
681
satish@chromium.orgce0b1d02011-01-25 07:17:11 +0000682 def ReadRequestBody(self):
683 """This function reads the body of the current HTTP request, handling
684 both plain and chunked transfer encoded requests."""
685
686 if self.headers.getheader('transfer-encoding') != 'chunked':
687 length = int(self.headers.getheader('content-length'))
688 return self.rfile.read(length)
689
690 # Read the request body as chunks.
691 body = ""
692 while True:
693 line = self.rfile.readline()
694 length = int(line, 16)
695 if length == 0:
696 self.rfile.readline()
697 break
698 body += self.rfile.read(length)
699 self.rfile.read(2)
700 return body
701
initial.commit94958cf2008-07-26 22:42:52 +0000702 def EchoHandler(self):
703 """This handler just echoes back the payload of the request, for testing
704 form submission."""
705
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000706 if not self._ShouldHandleRequest("/echo"):
initial.commit94958cf2008-07-26 22:42:52 +0000707 return False
708
709 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000710 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000711 self.end_headers()
satish@chromium.orgce0b1d02011-01-25 07:17:11 +0000712 self.wfile.write(self.ReadRequestBody())
initial.commit94958cf2008-07-26 22:42:52 +0000713 return True
714
715 def EchoTitleHandler(self):
716 """This handler is like Echo, but sets the page title to the request."""
717
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000718 if not self._ShouldHandleRequest("/echotitle"):
initial.commit94958cf2008-07-26 22:42:52 +0000719 return False
720
721 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000722 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000723 self.end_headers()
satish@chromium.orgce0b1d02011-01-25 07:17:11 +0000724 request = self.ReadRequestBody()
initial.commit94958cf2008-07-26 22:42:52 +0000725 self.wfile.write('<html><head><title>')
726 self.wfile.write(request)
727 self.wfile.write('</title></head></html>')
728 return True
729
730 def EchoAllHandler(self):
731 """This handler yields a (more) human-readable page listing information
732 about the request header & contents."""
733
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000734 if not self._ShouldHandleRequest("/echoall"):
initial.commit94958cf2008-07-26 22:42:52 +0000735 return False
736
737 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000738 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000739 self.end_headers()
740 self.wfile.write('<html><head><style>'
741 'pre { border: 1px solid black; margin: 5px; padding: 5px }'
742 '</style></head><body>'
743 '<div style="float: right">'
cbentzel@chromium.org0787bc72010-11-11 20:31:31 +0000744 '<a href="/echo">back to referring page</a></div>'
initial.commit94958cf2008-07-26 22:42:52 +0000745 '<h1>Request Body:</h1><pre>')
initial.commit94958cf2008-07-26 22:42:52 +0000746
ananta@chromium.org56d146f2010-01-11 19:03:01 +0000747 if self.command == 'POST' or self.command == 'PUT':
satish@chromium.orgce0b1d02011-01-25 07:17:11 +0000748 qs = self.ReadRequestBody()
ericroman@google.coma47622b2008-11-15 04:36:51 +0000749 params = cgi.parse_qs(qs, keep_blank_values=1)
750
751 for param in params:
752 self.wfile.write('%s=%s\n' % (param, params[param][0]))
initial.commit94958cf2008-07-26 22:42:52 +0000753
754 self.wfile.write('</pre>')
755
756 self.wfile.write('<h1>Request Headers:</h1><pre>%s</pre>' % self.headers)
757
758 self.wfile.write('</body></html>')
759 return True
760
761 def DownloadHandler(self):
762 """This handler sends a downloadable file with or without reporting
763 the size (6K)."""
764
765 if self.path.startswith("/download-unknown-size"):
766 send_length = False
767 elif self.path.startswith("/download-known-size"):
768 send_length = True
769 else:
770 return False
771
772 #
773 # The test which uses this functionality is attempting to send
774 # small chunks of data to the client. Use a fairly large buffer
775 # so that we'll fill chrome's IO buffer enough to force it to
776 # actually write the data.
777 # See also the comments in the client-side of this test in
778 # download_uitest.cc
779 #
780 size_chunk1 = 35*1024
781 size_chunk2 = 10*1024
782
783 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000784 self.send_header('Content-Type', 'application/octet-stream')
initial.commit94958cf2008-07-26 22:42:52 +0000785 self.send_header('Cache-Control', 'max-age=0')
786 if send_length:
787 self.send_header('Content-Length', size_chunk1 + size_chunk2)
788 self.end_headers()
789
790 # First chunk of data:
791 self.wfile.write("*" * size_chunk1)
792 self.wfile.flush()
793
794 # handle requests until one of them clears this flag.
795 self.server.waitForDownload = True
796 while self.server.waitForDownload:
797 self.server.handle_request()
798
799 # Second chunk of data:
800 self.wfile.write("*" * size_chunk2)
801 return True
802
803 def DownloadFinishHandler(self):
804 """This handler just tells the server to finish the current download."""
805
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000806 if not self._ShouldHandleRequest("/download-finish"):
initial.commit94958cf2008-07-26 22:42:52 +0000807 return False
808
809 self.server.waitForDownload = False
810 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000811 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +0000812 self.send_header('Cache-Control', 'max-age=0')
813 self.end_headers()
814 return True
815
cbentzel@chromium.orge30b32d2010-11-06 17:33:56 +0000816 def _ReplaceFileData(self, data, query_parameters):
817 """Replaces matching substrings in a file.
818
cbentzel@chromium.org099a3db2010-11-11 18:16:58 +0000819 If the 'replace_text' URL query parameter is present, it is expected to be
820 of the form old_text:new_text, which indicates that any old_text strings in
821 the file are replaced with new_text. Multiple 'replace_text' parameters may
822 be specified.
cbentzel@chromium.orge30b32d2010-11-06 17:33:56 +0000823
824 If the parameters are not present, |data| is returned.
825 """
826 query_dict = cgi.parse_qs(query_parameters)
cbentzel@chromium.org099a3db2010-11-11 18:16:58 +0000827 replace_text_values = query_dict.get('replace_text', [])
828 for replace_text_value in replace_text_values:
829 replace_text_args = replace_text_value.split(':')
830 if len(replace_text_args) != 2:
831 raise ValueError(
832 'replace_text must be of form old_text:new_text. Actual value: %s' %
833 replace_text_value)
834 old_text_b64, new_text_b64 = replace_text_args
835 old_text = base64.urlsafe_b64decode(old_text_b64)
836 new_text = base64.urlsafe_b64decode(new_text_b64)
837 data = data.replace(old_text, new_text)
838 return data
cbentzel@chromium.orge30b32d2010-11-06 17:33:56 +0000839
ahendrickson@chromium.orgab17b6a2011-05-24 20:14:39 +0000840 def ZipFileHandler(self):
841 """This handler sends the contents of the requested file in compressed form.
842 Can pass in a parameter that specifies that the content length be
843 C - the compressed size (OK),
844 U - the uncompressed size (Non-standard, but handled),
845 S - less than compressed (OK because we keep going),
846 M - larger than compressed but less than uncompressed (an error),
847 L - larger than uncompressed (an error)
848 Example: compressedfiles/Picture_1.doc?C
849 """
850
851 prefix = "/compressedfiles/"
852 if not self.path.startswith(prefix):
853 return False
854
855 # Consume a request body if present.
856 if self.command == 'POST' or self.command == 'PUT' :
857 self.ReadRequestBody()
858
859 _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
860
861 if not query in ('C', 'U', 'S', 'M', 'L'):
862 return False
863
864 sub_path = url_path[len(prefix):]
865 entries = sub_path.split('/')
866 file_path = os.path.join(self.server.data_dir, *entries)
867 if os.path.isdir(file_path):
868 file_path = os.path.join(file_path, 'index.html')
869
870 if not os.path.isfile(file_path):
871 print "File not found " + sub_path + " full path:" + file_path
872 self.send_error(404)
873 return True
874
875 f = open(file_path, "rb")
876 data = f.read()
877 uncompressed_len = len(data)
878 f.close()
879
880 # Compress the data.
881 data = zlib.compress(data)
882 compressed_len = len(data)
883
884 content_length = compressed_len
885 if query == 'U':
886 content_length = uncompressed_len
887 elif query == 'S':
888 content_length = compressed_len / 2
889 elif query == 'M':
890 content_length = (compressed_len + uncompressed_len) / 2
891 elif query == 'L':
892 content_length = compressed_len + uncompressed_len
893
894 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000895 self.send_header('Content-Type', 'application/msword')
ahendrickson@chromium.orgab17b6a2011-05-24 20:14:39 +0000896 self.send_header('Content-encoding', 'deflate')
897 self.send_header('Connection', 'close')
898 self.send_header('Content-Length', content_length)
899 self.send_header('ETag', '\'' + file_path + '\'')
900 self.end_headers()
901
902 self.wfile.write(data)
903
904 return True
905
initial.commit94958cf2008-07-26 22:42:52 +0000906 def FileHandler(self):
907 """This handler sends the contents of the requested file. Wow, it's like
908 a real webserver!"""
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +0000909 prefix = self.server.file_root_url
initial.commit94958cf2008-07-26 22:42:52 +0000910 if not self.path.startswith(prefix):
911 return False
darin@chromium.orgc25e5702009-07-23 19:10:23 +0000912 # Consume a request body if present.
ananta@chromium.org56d146f2010-01-11 19:03:01 +0000913 if self.command == 'POST' or self.command == 'PUT' :
satish@chromium.orgce0b1d02011-01-25 07:17:11 +0000914 self.ReadRequestBody()
cbentzel@chromium.org18fc5562012-01-13 13:27:44 +0000915 return self._FileHandlerHelper(prefix)
darin@chromium.orgc25e5702009-07-23 19:10:23 +0000916
cbentzel@chromium.org18fc5562012-01-13 13:27:44 +0000917 def PostOnlyFileHandler(self):
918 """This handler sends the contents of the requested file on a POST."""
cbentzel@chromium.org4f3a9412012-01-31 23:47:20 +0000919 prefix = urlparse.urljoin(self.server.file_root_url, 'post/')
cbentzel@chromium.org18fc5562012-01-13 13:27:44 +0000920 if not self.path.startswith(prefix):
921 return False
922 self.ReadRequestBody()
923 return self._FileHandlerHelper(prefix)
924
925 def _FileHandlerHelper(self, prefix):
simonjam@chromium.orgf9cf32f2012-02-13 23:56:14 +0000926 old_protocol_version = self.protocol_version
cbentzel@chromium.orge30b32d2010-11-06 17:33:56 +0000927 _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
928 sub_path = url_path[len(prefix):]
929 entries = sub_path.split('/')
930 file_path = os.path.join(self.server.data_dir, *entries)
931 if os.path.isdir(file_path):
932 file_path = os.path.join(file_path, 'index.html')
initial.commit94958cf2008-07-26 22:42:52 +0000933
cbentzel@chromium.orge30b32d2010-11-06 17:33:56 +0000934 if not os.path.isfile(file_path):
935 print "File not found " + sub_path + " full path:" + file_path
initial.commit94958cf2008-07-26 22:42:52 +0000936 self.send_error(404)
937 return True
938
cbentzel@chromium.orge30b32d2010-11-06 17:33:56 +0000939 f = open(file_path, "rb")
initial.commit94958cf2008-07-26 22:42:52 +0000940 data = f.read()
941 f.close()
942
cbentzel@chromium.orge30b32d2010-11-06 17:33:56 +0000943 data = self._ReplaceFileData(data, query)
944
initial.commit94958cf2008-07-26 22:42:52 +0000945 # If file.mock-http-headers exists, it contains the headers we
946 # should send. Read them in and parse them.
cbentzel@chromium.orge30b32d2010-11-06 17:33:56 +0000947 headers_path = file_path + '.mock-http-headers'
initial.commit94958cf2008-07-26 22:42:52 +0000948 if os.path.isfile(headers_path):
949 f = open(headers_path, "r")
950
951 # "HTTP/1.1 200 OK"
952 response = f.readline()
simonjam@chromium.orgf9cf32f2012-02-13 23:56:14 +0000953 http_major, http_minor, status_code = re.findall(
954 'HTTP/(\d+).(\d+) (\d+)', response)[0]
955 self.protocol_version = "HTTP/%s.%s" % (http_major, http_minor)
initial.commit94958cf2008-07-26 22:42:52 +0000956 self.send_response(int(status_code))
957
958 for line in f:
robertshield@chromium.org5e231612010-01-20 18:23:53 +0000959 header_values = re.findall('(\S+):\s*(.*)', line)
960 if len(header_values) > 0:
961 # "name: value"
962 name, value = header_values[0]
963 self.send_header(name, value)
initial.commit94958cf2008-07-26 22:42:52 +0000964 f.close()
965 else:
966 # Could be more generic once we support mime-type sniffing, but for
967 # now we need to set it explicitly.
jam@chromium.org41550782010-11-17 23:47:50 +0000968
969 range = self.headers.get('Range')
970 if range and range.startswith('bytes='):
shishir@chromium.orge6444822011-12-09 02:45:44 +0000971 # Note this doesn't handle all valid byte range values (i.e. left
972 # open ended ones), just enough for what we needed so far.
jam@chromium.org41550782010-11-17 23:47:50 +0000973 range = range[6:].split('-')
974 start = int(range[0])
shishir@chromium.orge6444822011-12-09 02:45:44 +0000975 if range[1]:
976 end = int(range[1])
977 else:
978 end = len(data)
jam@chromium.org41550782010-11-17 23:47:50 +0000979
980 self.send_response(206)
981 content_range = 'bytes ' + str(start) + '-' + str(end) + '/' + \
982 str(len(data))
983 self.send_header('Content-Range', content_range)
984 data = data[start: end + 1]
985 else:
986 self.send_response(200)
987
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000988 self.send_header('Content-Type', self.GetMIMETypeFromName(file_path))
jam@chromium.org41550782010-11-17 23:47:50 +0000989 self.send_header('Accept-Ranges', 'bytes')
initial.commit94958cf2008-07-26 22:42:52 +0000990 self.send_header('Content-Length', len(data))
jam@chromium.org41550782010-11-17 23:47:50 +0000991 self.send_header('ETag', '\'' + file_path + '\'')
initial.commit94958cf2008-07-26 22:42:52 +0000992 self.end_headers()
993
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +0000994 if (self.command != 'HEAD'):
995 self.wfile.write(data)
initial.commit94958cf2008-07-26 22:42:52 +0000996
simonjam@chromium.orgf9cf32f2012-02-13 23:56:14 +0000997 self.protocol_version = old_protocol_version
initial.commit94958cf2008-07-26 22:42:52 +0000998 return True
999
levin@chromium.orgf7ee2e42009-08-26 02:33:46 +00001000 def SetCookieHandler(self):
1001 """This handler just sets a cookie, for testing cookie handling."""
1002
1003 if not self._ShouldHandleRequest("/set-cookie"):
1004 return False
1005
1006 query_char = self.path.find('?')
1007 if query_char != -1:
1008 cookie_values = self.path[query_char + 1:].split('&')
1009 else:
1010 cookie_values = ("",)
1011 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001012 self.send_header('Content-Type', 'text/html')
levin@chromium.orgf7ee2e42009-08-26 02:33:46 +00001013 for cookie_value in cookie_values:
1014 self.send_header('Set-Cookie', '%s' % cookie_value)
1015 self.end_headers()
1016 for cookie_value in cookie_values:
1017 self.wfile.write('%s' % cookie_value)
1018 return True
1019
battre@chromium.orgd4479e12011-11-07 17:09:19 +00001020 def SetHeaderHandler(self):
1021 """This handler sets a response header. Parameters are in the
1022 key%3A%20value&key2%3A%20value2 format."""
1023
1024 if not self._ShouldHandleRequest("/set-header"):
1025 return False
1026
1027 query_char = self.path.find('?')
1028 if query_char != -1:
1029 headers_values = self.path[query_char + 1:].split('&')
1030 else:
1031 headers_values = ("",)
1032 self.send_response(200)
1033 self.send_header('Content-Type', 'text/html')
1034 for header_value in headers_values:
1035 header_value = urllib.unquote(header_value)
1036 (key, value) = header_value.split(': ', 1)
1037 self.send_header(key, value)
1038 self.end_headers()
1039 for header_value in headers_values:
1040 self.wfile.write('%s' % header_value)
1041 return True
1042
initial.commit94958cf2008-07-26 22:42:52 +00001043 def AuthBasicHandler(self):
1044 """This handler tests 'Basic' authentication. It just sends a page with
1045 title 'user/pass' if you succeed."""
1046
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +00001047 if not self._ShouldHandleRequest("/auth-basic"):
initial.commit94958cf2008-07-26 22:42:52 +00001048 return False
1049
1050 username = userpass = password = b64str = ""
cbentzel@chromium.org5a808d22011-01-05 15:51:24 +00001051 expected_password = 'secret'
1052 realm = 'testrealm'
1053 set_cookie_if_challenged = False
initial.commit94958cf2008-07-26 22:42:52 +00001054
cbentzel@chromium.org5a808d22011-01-05 15:51:24 +00001055 _, _, url_path, _, query, _ = urlparse.urlparse(self.path)
1056 query_params = cgi.parse_qs(query, True)
1057 if 'set-cookie-if-challenged' in query_params:
1058 set_cookie_if_challenged = True
1059 if 'password' in query_params:
1060 expected_password = query_params['password'][0]
1061 if 'realm' in query_params:
1062 realm = query_params['realm'][0]
ericroman@google.com239b4d82009-03-27 04:00:22 +00001063
initial.commit94958cf2008-07-26 22:42:52 +00001064 auth = self.headers.getheader('authorization')
1065 try:
1066 if not auth:
1067 raise Exception('no auth')
1068 b64str = re.findall(r'Basic (\S+)', auth)[0]
1069 userpass = base64.b64decode(b64str)
1070 username, password = re.findall(r'([^:]+):(\S+)', userpass)[0]
cbentzel@chromium.org5a808d22011-01-05 15:51:24 +00001071 if password != expected_password:
initial.commit94958cf2008-07-26 22:42:52 +00001072 raise Exception('wrong password')
1073 except Exception, e:
1074 # Authentication failed.
1075 self.send_response(401)
cbentzel@chromium.org5a808d22011-01-05 15:51:24 +00001076 self.send_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001077 self.send_header('Content-Type', 'text/html')
ericroman@google.com239b4d82009-03-27 04:00:22 +00001078 if set_cookie_if_challenged:
1079 self.send_header('Set-Cookie', 'got_challenged=true')
initial.commit94958cf2008-07-26 22:42:52 +00001080 self.end_headers()
1081 self.wfile.write('<html><head>')
1082 self.wfile.write('<title>Denied: %s</title>' % e)
1083 self.wfile.write('</head><body>')
1084 self.wfile.write('auth=%s<p>' % auth)
1085 self.wfile.write('b64str=%s<p>' % b64str)
1086 self.wfile.write('username: %s<p>' % username)
1087 self.wfile.write('userpass: %s<p>' % userpass)
1088 self.wfile.write('password: %s<p>' % password)
1089 self.wfile.write('You sent:<br>%s<p>' % self.headers)
1090 self.wfile.write('</body></html>')
1091 return True
1092
1093 # Authentication successful. (Return a cachable response to allow for
1094 # testing cached pages that require authentication.)
rvargas@google.com54453b72011-05-19 01:11:11 +00001095 old_protocol_version = self.protocol_version
1096 self.protocol_version = "HTTP/1.1"
1097
initial.commit94958cf2008-07-26 22:42:52 +00001098 if_none_match = self.headers.getheader('if-none-match')
1099 if if_none_match == "abc":
1100 self.send_response(304)
1101 self.end_headers()
cbentzel@chromium.org5a808d22011-01-05 15:51:24 +00001102 elif url_path.endswith(".gif"):
1103 # Using chrome/test/data/google/logo.gif as the test image
1104 test_image_path = ['google', 'logo.gif']
1105 gif_path = os.path.join(self.server.data_dir, *test_image_path)
1106 if not os.path.isfile(gif_path):
1107 self.send_error(404)
rvargas@google.com54453b72011-05-19 01:11:11 +00001108 self.protocol_version = old_protocol_version
cbentzel@chromium.org5a808d22011-01-05 15:51:24 +00001109 return True
1110
1111 f = open(gif_path, "rb")
1112 data = f.read()
1113 f.close()
1114
1115 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001116 self.send_header('Content-Type', 'image/gif')
cbentzel@chromium.org5a808d22011-01-05 15:51:24 +00001117 self.send_header('Cache-control', 'max-age=60000')
1118 self.send_header('Etag', 'abc')
1119 self.end_headers()
1120 self.wfile.write(data)
initial.commit94958cf2008-07-26 22:42:52 +00001121 else:
1122 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001123 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +00001124 self.send_header('Cache-control', 'max-age=60000')
1125 self.send_header('Etag', 'abc')
1126 self.end_headers()
1127 self.wfile.write('<html><head>')
1128 self.wfile.write('<title>%s/%s</title>' % (username, password))
1129 self.wfile.write('</head><body>')
1130 self.wfile.write('auth=%s<p>' % auth)
ericroman@google.com239b4d82009-03-27 04:00:22 +00001131 self.wfile.write('You sent:<br>%s<p>' % self.headers)
initial.commit94958cf2008-07-26 22:42:52 +00001132 self.wfile.write('</body></html>')
1133
rvargas@google.com54453b72011-05-19 01:11:11 +00001134 self.protocol_version = old_protocol_version
initial.commit94958cf2008-07-26 22:42:52 +00001135 return True
1136
tonyg@chromium.org75054202010-03-31 22:06:10 +00001137 def GetNonce(self, force_reset=False):
1138 """Returns a nonce that's stable per request path for the server's lifetime.
initial.commit94958cf2008-07-26 22:42:52 +00001139
tonyg@chromium.org75054202010-03-31 22:06:10 +00001140 This is a fake implementation. A real implementation would only use a given
1141 nonce a single time (hence the name n-once). However, for the purposes of
1142 unittesting, we don't care about the security of the nonce.
1143
1144 Args:
1145 force_reset: Iff set, the nonce will be changed. Useful for testing the
1146 "stale" response.
1147 """
1148 if force_reset or not self.server.nonce_time:
1149 self.server.nonce_time = time.time()
1150 return _new_md5('privatekey%s%d' %
1151 (self.path, self.server.nonce_time)).hexdigest()
1152
1153 def AuthDigestHandler(self):
1154 """This handler tests 'Digest' authentication.
1155
1156 It just sends a page with title 'user/pass' if you succeed.
1157
1158 A stale response is sent iff "stale" is present in the request path.
1159 """
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +00001160 if not self._ShouldHandleRequest("/auth-digest"):
initial.commit94958cf2008-07-26 22:42:52 +00001161 return False
1162
tonyg@chromium.org75054202010-03-31 22:06:10 +00001163 stale = 'stale' in self.path
1164 nonce = self.GetNonce(force_reset=stale)
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +00001165 opaque = _new_md5('opaque').hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +00001166 password = 'secret'
1167 realm = 'testrealm'
1168
1169 auth = self.headers.getheader('authorization')
1170 pairs = {}
1171 try:
1172 if not auth:
1173 raise Exception('no auth')
1174 if not auth.startswith('Digest'):
1175 raise Exception('not digest')
1176 # Pull out all the name="value" pairs as a dictionary.
1177 pairs = dict(re.findall(r'(\b[^ ,=]+)="?([^",]+)"?', auth))
1178
1179 # Make sure it's all valid.
1180 if pairs['nonce'] != nonce:
1181 raise Exception('wrong nonce')
1182 if pairs['opaque'] != opaque:
1183 raise Exception('wrong opaque')
1184
1185 # Check the 'response' value and make sure it matches our magic hash.
1186 # See http://www.ietf.org/rfc/rfc2617.txt
maruel@google.come250a9b2009-03-10 17:39:46 +00001187 hash_a1 = _new_md5(
1188 ':'.join([pairs['username'], realm, password])).hexdigest()
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +00001189 hash_a2 = _new_md5(':'.join([self.command, pairs['uri']])).hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +00001190 if 'qop' in pairs and 'nc' in pairs and 'cnonce' in pairs:
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +00001191 response = _new_md5(':'.join([hash_a1, nonce, pairs['nc'],
initial.commit94958cf2008-07-26 22:42:52 +00001192 pairs['cnonce'], pairs['qop'], hash_a2])).hexdigest()
1193 else:
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +00001194 response = _new_md5(':'.join([hash_a1, nonce, hash_a2])).hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +00001195
1196 if pairs['response'] != response:
1197 raise Exception('wrong password')
1198 except Exception, e:
1199 # Authentication failed.
1200 self.send_response(401)
1201 hdr = ('Digest '
1202 'realm="%s", '
1203 'domain="/", '
1204 'qop="auth", '
1205 'algorithm=MD5, '
1206 'nonce="%s", '
1207 'opaque="%s"') % (realm, nonce, opaque)
1208 if stale:
1209 hdr += ', stale="TRUE"'
1210 self.send_header('WWW-Authenticate', hdr)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001211 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +00001212 self.end_headers()
1213 self.wfile.write('<html><head>')
1214 self.wfile.write('<title>Denied: %s</title>' % e)
1215 self.wfile.write('</head><body>')
1216 self.wfile.write('auth=%s<p>' % auth)
1217 self.wfile.write('pairs=%s<p>' % pairs)
1218 self.wfile.write('You sent:<br>%s<p>' % self.headers)
1219 self.wfile.write('We are replying:<br>%s<p>' % hdr)
1220 self.wfile.write('</body></html>')
1221 return True
1222
1223 # Authentication successful.
1224 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001225 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +00001226 self.end_headers()
1227 self.wfile.write('<html><head>')
1228 self.wfile.write('<title>%s/%s</title>' % (pairs['username'], password))
1229 self.wfile.write('</head><body>')
1230 self.wfile.write('auth=%s<p>' % auth)
1231 self.wfile.write('pairs=%s<p>' % pairs)
1232 self.wfile.write('</body></html>')
1233
1234 return True
1235
1236 def SlowServerHandler(self):
1237 """Wait for the user suggested time before responding. The syntax is
1238 /slow?0.5 to wait for half a second."""
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +00001239 if not self._ShouldHandleRequest("/slow"):
initial.commit94958cf2008-07-26 22:42:52 +00001240 return False
1241 query_char = self.path.find('?')
1242 wait_sec = 1.0
1243 if query_char >= 0:
1244 try:
1245 wait_sec = int(self.path[query_char + 1:])
1246 except ValueError:
1247 pass
1248 time.sleep(wait_sec)
1249 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001250 self.send_header('Content-Type', 'text/plain')
initial.commit94958cf2008-07-26 22:42:52 +00001251 self.end_headers()
1252 self.wfile.write("waited %d seconds" % wait_sec)
1253 return True
1254
vsevik@chromium.orgf0e997e2011-05-20 09:36:14 +00001255 def ChunkedServerHandler(self):
1256 """Send chunked response. Allows to specify chunks parameters:
1257 - waitBeforeHeaders - ms to wait before sending headers
1258 - waitBetweenChunks - ms to wait between chunks
1259 - chunkSize - size of each chunk in bytes
1260 - chunksNumber - number of chunks
1261 Example: /chunked?waitBeforeHeaders=1000&chunkSize=5&chunksNumber=5
1262 waits one second, then sends headers and five chunks five bytes each."""
1263 if not self._ShouldHandleRequest("/chunked"):
1264 return False
1265 query_char = self.path.find('?')
1266 chunkedSettings = {'waitBeforeHeaders' : 0,
1267 'waitBetweenChunks' : 0,
1268 'chunkSize' : 5,
1269 'chunksNumber' : 5}
1270 if query_char >= 0:
1271 params = self.path[query_char + 1:].split('&')
1272 for param in params:
1273 keyValue = param.split('=')
1274 if len(keyValue) == 2:
1275 try:
1276 chunkedSettings[keyValue[0]] = int(keyValue[1])
1277 except ValueError:
1278 pass
1279 time.sleep(0.001 * chunkedSettings['waitBeforeHeaders']);
1280 self.protocol_version = 'HTTP/1.1' # Needed for chunked encoding
1281 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001282 self.send_header('Content-Type', 'text/plain')
vsevik@chromium.orgf0e997e2011-05-20 09:36:14 +00001283 self.send_header('Connection', 'close')
1284 self.send_header('Transfer-Encoding', 'chunked')
1285 self.end_headers()
1286 # Chunked encoding: sending all chunks, then final zero-length chunk and
1287 # then final CRLF.
1288 for i in range(0, chunkedSettings['chunksNumber']):
1289 if i > 0:
1290 time.sleep(0.001 * chunkedSettings['waitBetweenChunks'])
1291 self.sendChunkHelp('*' * chunkedSettings['chunkSize'])
1292 self.wfile.flush(); # Keep in mind that we start flushing only after 1kb.
1293 self.sendChunkHelp('')
1294 return True
1295
initial.commit94958cf2008-07-26 22:42:52 +00001296 def ContentTypeHandler(self):
1297 """Returns a string of html with the given content type. E.g.,
1298 /contenttype?text/css returns an html file with the Content-Type
1299 header set to text/css."""
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +00001300 if not self._ShouldHandleRequest("/contenttype"):
initial.commit94958cf2008-07-26 22:42:52 +00001301 return False
1302 query_char = self.path.find('?')
1303 content_type = self.path[query_char + 1:].strip()
1304 if not content_type:
1305 content_type = 'text/html'
1306 self.send_response(200)
1307 self.send_header('Content-Type', content_type)
1308 self.end_headers()
1309 self.wfile.write("<html>\n<body>\n<p>HTML text</p>\n</body>\n</html>\n");
1310 return True
1311
creis@google.com2f4f6a42011-03-25 19:44:19 +00001312 def NoContentHandler(self):
1313 """Returns a 204 No Content response."""
1314 if not self._ShouldHandleRequest("/nocontent"):
1315 return False
1316 self.send_response(204)
1317 self.end_headers()
1318 return True
1319
initial.commit94958cf2008-07-26 22:42:52 +00001320 def ServerRedirectHandler(self):
1321 """Sends a server redirect to the given URL. The syntax is
maruel@google.come250a9b2009-03-10 17:39:46 +00001322 '/server-redirect?http://foo.bar/asdf' to redirect to
1323 'http://foo.bar/asdf'"""
initial.commit94958cf2008-07-26 22:42:52 +00001324
1325 test_name = "/server-redirect"
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +00001326 if not self._ShouldHandleRequest(test_name):
initial.commit94958cf2008-07-26 22:42:52 +00001327 return False
1328
1329 query_char = self.path.find('?')
1330 if query_char < 0 or len(self.path) <= query_char + 1:
1331 self.sendRedirectHelp(test_name)
1332 return True
1333 dest = self.path[query_char + 1:]
1334
1335 self.send_response(301) # moved permanently
1336 self.send_header('Location', dest)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001337 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +00001338 self.end_headers()
1339 self.wfile.write('<html><head>')
1340 self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
1341
wtc@chromium.org743d77b2009-02-11 02:48:15 +00001342 return True
initial.commit94958cf2008-07-26 22:42:52 +00001343
1344 def ClientRedirectHandler(self):
1345 """Sends a client redirect to the given URL. The syntax is
maruel@google.come250a9b2009-03-10 17:39:46 +00001346 '/client-redirect?http://foo.bar/asdf' to redirect to
1347 'http://foo.bar/asdf'"""
initial.commit94958cf2008-07-26 22:42:52 +00001348
1349 test_name = "/client-redirect"
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +00001350 if not self._ShouldHandleRequest(test_name):
initial.commit94958cf2008-07-26 22:42:52 +00001351 return False
1352
1353 query_char = self.path.find('?');
1354 if query_char < 0 or len(self.path) <= query_char + 1:
1355 self.sendRedirectHelp(test_name)
1356 return True
1357 dest = self.path[query_char + 1:]
1358
1359 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001360 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +00001361 self.end_headers()
1362 self.wfile.write('<html><head>')
1363 self.wfile.write('<meta http-equiv="refresh" content="0;url=%s">' % dest)
1364 self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
1365
1366 return True
1367
tony@chromium.org03266982010-03-05 03:18:42 +00001368 def MultipartHandler(self):
1369 """Send a multipart response (10 text/html pages)."""
tony@chromium.org4cb88302011-09-27 22:13:49 +00001370 test_name = '/multipart'
tony@chromium.org03266982010-03-05 03:18:42 +00001371 if not self._ShouldHandleRequest(test_name):
1372 return False
1373
1374 num_frames = 10
1375 bound = '12345'
1376 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001377 self.send_header('Content-Type',
tony@chromium.org03266982010-03-05 03:18:42 +00001378 'multipart/x-mixed-replace;boundary=' + bound)
1379 self.end_headers()
1380
1381 for i in xrange(num_frames):
1382 self.wfile.write('--' + bound + '\r\n')
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001383 self.wfile.write('Content-Type: text/html\r\n\r\n')
tony@chromium.org03266982010-03-05 03:18:42 +00001384 self.wfile.write('<title>page ' + str(i) + '</title>')
1385 self.wfile.write('page ' + str(i))
1386
1387 self.wfile.write('--' + bound + '--')
1388 return True
1389
tony@chromium.org4cb88302011-09-27 22:13:49 +00001390 def MultipartSlowHandler(self):
1391 """Send a multipart response (3 text/html pages) with a slight delay
1392 between each page. This is similar to how some pages show status using
1393 multipart."""
1394 test_name = '/multipart-slow'
1395 if not self._ShouldHandleRequest(test_name):
1396 return False
1397
1398 num_frames = 3
1399 bound = '12345'
1400 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001401 self.send_header('Content-Type',
tony@chromium.org4cb88302011-09-27 22:13:49 +00001402 'multipart/x-mixed-replace;boundary=' + bound)
1403 self.end_headers()
1404
1405 for i in xrange(num_frames):
1406 self.wfile.write('--' + bound + '\r\n')
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001407 self.wfile.write('Content-Type: text/html\r\n\r\n')
tony@chromium.org4cb88302011-09-27 22:13:49 +00001408 time.sleep(0.25)
1409 if i == 2:
1410 self.wfile.write('<title>PASS</title>')
1411 else:
1412 self.wfile.write('<title>page ' + str(i) + '</title>')
1413 self.wfile.write('page ' + str(i) + '<!-- ' + ('x' * 2048) + '-->')
1414
1415 self.wfile.write('--' + bound + '--')
1416 return True
1417
agl@chromium.orgf9e66792011-12-12 22:22:19 +00001418 def GetSSLSessionCacheHandler(self):
1419 """Send a reply containing a log of the session cache operations."""
1420
1421 if not self._ShouldHandleRequest('/ssl-session-cache'):
1422 return False
1423
1424 self.send_response(200)
1425 self.send_header('Content-Type', 'text/plain')
1426 self.end_headers()
1427 try:
1428 for (action, sessionID) in self.server.session_cache.log:
1429 self.wfile.write('%s\t%s\n' % (action, sessionID.encode('hex')))
1430 except AttributeError, e:
1431 self.wfile.write('Pass --https-record-resume in order to use' +
1432 ' this request')
1433 return True
1434
simonjam@chromium.orgf9cf32f2012-02-13 23:56:14 +00001435 def CloseSocketHandler(self):
1436 """Closes the socket without sending anything."""
1437
1438 if not self._ShouldHandleRequest('/close-socket'):
1439 return False
1440
1441 self.wfile.close()
1442 return True
1443
initial.commit94958cf2008-07-26 22:42:52 +00001444 def DefaultResponseHandler(self):
1445 """This is the catch-all response handler for requests that aren't handled
1446 by one of the special handlers above.
1447 Note that we specify the content-length as without it the https connection
1448 is not closed properly (and the browser keeps expecting data)."""
1449
1450 contents = "Default response given for path: " + self.path
1451 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001452 self.send_header('Content-Type', 'text/html')
1453 self.send_header('Content-Length', len(contents))
initial.commit94958cf2008-07-26 22:42:52 +00001454 self.end_headers()
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001455 if (self.command != 'HEAD'):
1456 self.wfile.write(contents)
initial.commit94958cf2008-07-26 22:42:52 +00001457 return True
1458
wtc@chromium.org743d77b2009-02-11 02:48:15 +00001459 def RedirectConnectHandler(self):
1460 """Sends a redirect to the CONNECT request for www.redirect.com. This
1461 response is not specified by the RFC, so the browser should not follow
1462 the redirect."""
1463
1464 if (self.path.find("www.redirect.com") < 0):
1465 return False
1466
1467 dest = "http://www.destination.com/foo.js"
1468
1469 self.send_response(302) # moved temporarily
1470 self.send_header('Location', dest)
1471 self.send_header('Connection', 'close')
1472 self.end_headers()
1473 return True
1474
wtc@chromium.orgb86c7f92009-02-14 01:45:08 +00001475 def ServerAuthConnectHandler(self):
1476 """Sends a 401 to the CONNECT request for www.server-auth.com. This
1477 response doesn't make sense because the proxy server cannot request
1478 server authentication."""
1479
1480 if (self.path.find("www.server-auth.com") < 0):
1481 return False
1482
1483 challenge = 'Basic realm="WallyWorld"'
1484
1485 self.send_response(401) # unauthorized
1486 self.send_header('WWW-Authenticate', challenge)
1487 self.send_header('Connection', 'close')
1488 self.end_headers()
1489 return True
wtc@chromium.org743d77b2009-02-11 02:48:15 +00001490
1491 def DefaultConnectResponseHandler(self):
1492 """This is the catch-all response handler for CONNECT requests that aren't
1493 handled by one of the special handlers above. Real Web servers respond
1494 with 400 to CONNECT requests."""
1495
1496 contents = "Your client has issued a malformed or illegal request."
1497 self.send_response(400) # bad request
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001498 self.send_header('Content-Type', 'text/html')
1499 self.send_header('Content-Length', len(contents))
wtc@chromium.org743d77b2009-02-11 02:48:15 +00001500 self.end_headers()
1501 self.wfile.write(contents)
1502 return True
1503
mnissler@chromium.org7c939802010-11-11 08:47:14 +00001504 def DeviceManagementHandler(self):
1505 """Delegates to the device management service used for cloud policy."""
1506 if not self._ShouldHandleRequest("/device_management"):
1507 return False
1508
satish@chromium.orgce0b1d02011-01-25 07:17:11 +00001509 raw_request = self.ReadRequestBody()
mnissler@chromium.org7c939802010-11-11 08:47:14 +00001510
1511 if not self.server._device_management_handler:
1512 import device_management
1513 policy_path = os.path.join(self.server.data_dir, 'device_management')
1514 self.server._device_management_handler = (
gfeher@chromium.orge0bddc12011-01-28 18:15:24 +00001515 device_management.TestServer(policy_path,
mnissler@chromium.orgcfe39282011-04-11 12:13:05 +00001516 self.server.policy_keys,
1517 self.server.policy_user))
mnissler@chromium.org7c939802010-11-11 08:47:14 +00001518
1519 http_response, raw_reply = (
1520 self.server._device_management_handler.HandleRequest(self.path,
1521 self.headers,
1522 raw_request))
1523 self.send_response(http_response)
joaodasilva@chromium.orgd3a1ca52011-09-28 20:46:20 +00001524 if (http_response == 200):
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001525 self.send_header('Content-Type', 'application/x-protobuffer')
mnissler@chromium.org7c939802010-11-11 08:47:14 +00001526 self.end_headers()
1527 self.wfile.write(raw_reply)
1528 return True
1529
initial.commit94958cf2008-07-26 22:42:52 +00001530 # called by the redirect handling function when there is no parameter
1531 def sendRedirectHelp(self, redirect_name):
1532 self.send_response(200)
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001533 self.send_header('Content-Type', 'text/html')
initial.commit94958cf2008-07-26 22:42:52 +00001534 self.end_headers()
1535 self.wfile.write('<html><body><h1>Error: no redirect destination</h1>')
1536 self.wfile.write('Use <pre>%s?http://dest...</pre>' % redirect_name)
1537 self.wfile.write('</body></html>')
1538
vsevik@chromium.orgf0e997e2011-05-20 09:36:14 +00001539 # called by chunked handling function
1540 def sendChunkHelp(self, chunk):
1541 # Each chunk consists of: chunk size (hex), CRLF, chunk body, CRLF
1542 self.wfile.write('%X\r\n' % len(chunk))
1543 self.wfile.write(chunk)
1544 self.wfile.write('\r\n')
1545
akalin@chromium.org154bb132010-11-12 02:20:27 +00001546
1547class SyncPageHandler(BasePageHandler):
1548 """Handler for the main HTTP sync server."""
1549
1550 def __init__(self, request, client_address, sync_http_server):
nick@chromium.org45dcfa82011-04-23 01:53:16 +00001551 get_handlers = [self.ChromiumSyncMigrationOpHandler,
lipalani@chromium.orgb56c12b2011-07-30 02:17:37 +00001552 self.ChromiumSyncTimeHandler,
akalin@chromium.org0332ec72011-08-27 06:53:21 +00001553 self.ChromiumSyncDisableNotificationsOpHandler,
1554 self.ChromiumSyncEnableNotificationsOpHandler,
1555 self.ChromiumSyncSendNotificationOpHandler,
lipalani@chromium.orgf9737fc2011-08-12 05:37:47 +00001556 self.ChromiumSyncBirthdayErrorOpHandler,
zea@chromium.org1aa5e632011-08-25 22:21:02 +00001557 self.ChromiumSyncTransientErrorOpHandler,
lipalani@chromium.orgbabf5892011-09-22 23:14:08 +00001558 self.ChromiumSyncSyncTabsOpHandler,
lipalani@chromium.org0f7e6092011-09-23 01:26:10 +00001559 self.ChromiumSyncErrorOpHandler,
1560 self.ChromiumSyncCredHandler]
lipalani@chromium.orgf9737fc2011-08-12 05:37:47 +00001561
nick@chromium.org45dcfa82011-04-23 01:53:16 +00001562 post_handlers = [self.ChromiumSyncCommandHandler,
1563 self.ChromiumSyncTimeHandler]
akalin@chromium.org154bb132010-11-12 02:20:27 +00001564 BasePageHandler.__init__(self, request, client_address,
mmenke@chromium.orgbfff75b2011-11-01 02:32:05 +00001565 sync_http_server, [], get_handlers, [],
akalin@chromium.org154bb132010-11-12 02:20:27 +00001566 post_handlers, [])
1567
lipalani@chromium.org0f7e6092011-09-23 01:26:10 +00001568
akalin@chromium.org154bb132010-11-12 02:20:27 +00001569 def ChromiumSyncTimeHandler(self):
1570 """Handle Chromium sync .../time requests.
1571
1572 The syncer sometimes checks server reachability by examining /time.
1573 """
1574 test_name = "/chromiumsync/time"
1575 if not self._ShouldHandleRequest(test_name):
1576 return False
1577
nick@chromium.org45dcfa82011-04-23 01:53:16 +00001578 # Chrome hates it if we send a response before reading the request.
1579 if self.headers.getheader('content-length'):
1580 length = int(self.headers.getheader('content-length'))
1581 raw_request = self.rfile.read(length)
1582
akalin@chromium.org154bb132010-11-12 02:20:27 +00001583 self.send_response(200)
nick@chromium.org45dcfa82011-04-23 01:53:16 +00001584 self.send_header('Content-Type', 'text/plain')
akalin@chromium.org154bb132010-11-12 02:20:27 +00001585 self.end_headers()
nick@chromium.org45dcfa82011-04-23 01:53:16 +00001586 self.wfile.write('0123456789')
akalin@chromium.org154bb132010-11-12 02:20:27 +00001587 return True
1588
1589 def ChromiumSyncCommandHandler(self):
1590 """Handle a chromiumsync command arriving via http.
1591
1592 This covers all sync protocol commands: authentication, getupdates, and
1593 commit.
1594 """
1595 test_name = "/chromiumsync/command"
1596 if not self._ShouldHandleRequest(test_name):
1597 return False
1598
satish@chromium.orgc5228b12011-01-25 12:13:19 +00001599 length = int(self.headers.getheader('content-length'))
1600 raw_request = self.rfile.read(length)
lipalani@chromium.org0f7e6092011-09-23 01:26:10 +00001601 http_response = 200
1602 raw_reply = None
1603 if not self.server.GetAuthenticated():
1604 http_response = 401
1605 challenge = 'GoogleLogin realm="http://127.0.0.1", service="chromiumsync"'
1606 else:
1607 http_response, raw_reply = self.server.HandleCommand(
1608 self.path, raw_request)
akalin@chromium.org154bb132010-11-12 02:20:27 +00001609
lipalani@chromium.org0f7e6092011-09-23 01:26:10 +00001610 ### Now send the response to the client. ###
akalin@chromium.org154bb132010-11-12 02:20:27 +00001611 self.send_response(http_response)
lipalani@chromium.org0f7e6092011-09-23 01:26:10 +00001612 if http_response == 401:
1613 self.send_header('www-Authenticate', challenge)
akalin@chromium.org154bb132010-11-12 02:20:27 +00001614 self.end_headers()
1615 self.wfile.write(raw_reply)
1616 return True
1617
nick@chromium.org45dcfa82011-04-23 01:53:16 +00001618 def ChromiumSyncMigrationOpHandler(self):
nick@chromium.org45dcfa82011-04-23 01:53:16 +00001619 test_name = "/chromiumsync/migrate"
1620 if not self._ShouldHandleRequest(test_name):
1621 return False
1622
1623 http_response, raw_reply = self.server._sync_handler.HandleMigrate(
1624 self.path)
1625 self.send_response(http_response)
1626 self.send_header('Content-Type', 'text/html')
1627 self.send_header('Content-Length', len(raw_reply))
1628 self.end_headers()
1629 self.wfile.write(raw_reply)
1630 return True
1631
lipalani@chromium.org0f7e6092011-09-23 01:26:10 +00001632 def ChromiumSyncCredHandler(self):
1633 test_name = "/chromiumsync/cred"
1634 if not self._ShouldHandleRequest(test_name):
1635 return False
1636 try:
1637 query = urlparse.urlparse(self.path)[4]
1638 cred_valid = urlparse.parse_qs(query)['valid']
1639 if cred_valid[0] == 'True':
1640 self.server.SetAuthenticated(True)
1641 else:
1642 self.server.SetAuthenticated(False)
1643 except:
1644 self.server.SetAuthenticated(False)
1645
1646 http_response = 200
1647 raw_reply = 'Authenticated: %s ' % self.server.GetAuthenticated()
1648 self.send_response(http_response)
1649 self.send_header('Content-Type', 'text/html')
1650 self.send_header('Content-Length', len(raw_reply))
1651 self.end_headers()
1652 self.wfile.write(raw_reply)
1653 return True
1654
akalin@chromium.org0332ec72011-08-27 06:53:21 +00001655 def ChromiumSyncDisableNotificationsOpHandler(self):
1656 test_name = "/chromiumsync/disablenotifications"
1657 if not self._ShouldHandleRequest(test_name):
1658 return False
1659 self.server.GetXmppServer().DisableNotifications()
1660 result = 200
1661 raw_reply = ('<html><title>Notifications disabled</title>'
1662 '<H1>Notifications disabled</H1></html>')
1663 self.send_response(result)
1664 self.send_header('Content-Type', 'text/html')
1665 self.send_header('Content-Length', len(raw_reply))
1666 self.end_headers()
1667 self.wfile.write(raw_reply)
1668 return True;
1669
1670 def ChromiumSyncEnableNotificationsOpHandler(self):
1671 test_name = "/chromiumsync/enablenotifications"
1672 if not self._ShouldHandleRequest(test_name):
1673 return False
1674 self.server.GetXmppServer().EnableNotifications()
1675 result = 200
1676 raw_reply = ('<html><title>Notifications enabled</title>'
1677 '<H1>Notifications enabled</H1></html>')
1678 self.send_response(result)
1679 self.send_header('Content-Type', 'text/html')
1680 self.send_header('Content-Length', len(raw_reply))
1681 self.end_headers()
1682 self.wfile.write(raw_reply)
1683 return True;
1684
1685 def ChromiumSyncSendNotificationOpHandler(self):
1686 test_name = "/chromiumsync/sendnotification"
1687 if not self._ShouldHandleRequest(test_name):
1688 return False
1689 query = urlparse.urlparse(self.path)[4]
1690 query_params = urlparse.parse_qs(query)
1691 channel = ''
1692 data = ''
1693 if 'channel' in query_params:
1694 channel = query_params['channel'][0]
1695 if 'data' in query_params:
1696 data = query_params['data'][0]
1697 self.server.GetXmppServer().SendNotification(channel, data)
1698 result = 200
1699 raw_reply = ('<html><title>Notification sent</title>'
1700 '<H1>Notification sent with channel "%s" '
1701 'and data "%s"</H1></html>'
1702 % (channel, data))
1703 self.send_response(result)
1704 self.send_header('Content-Type', 'text/html')
1705 self.send_header('Content-Length', len(raw_reply))
1706 self.end_headers()
1707 self.wfile.write(raw_reply)
1708 return True;
1709
lipalani@chromium.orgb56c12b2011-07-30 02:17:37 +00001710 def ChromiumSyncBirthdayErrorOpHandler(self):
1711 test_name = "/chromiumsync/birthdayerror"
1712 if not self._ShouldHandleRequest(test_name):
1713 return False
1714 result, raw_reply = self.server._sync_handler.HandleCreateBirthdayError()
1715 self.send_response(result)
1716 self.send_header('Content-Type', 'text/html')
1717 self.send_header('Content-Length', len(raw_reply))
1718 self.end_headers()
1719 self.wfile.write(raw_reply)
1720 return True;
1721
lipalani@chromium.orgf9737fc2011-08-12 05:37:47 +00001722 def ChromiumSyncTransientErrorOpHandler(self):
1723 test_name = "/chromiumsync/transienterror"
1724 if not self._ShouldHandleRequest(test_name):
1725 return False
1726 result, raw_reply = self.server._sync_handler.HandleSetTransientError()
1727 self.send_response(result)
1728 self.send_header('Content-Type', 'text/html')
1729 self.send_header('Content-Length', len(raw_reply))
1730 self.end_headers()
1731 self.wfile.write(raw_reply)
1732 return True;
1733
lipalani@chromium.orgbabf5892011-09-22 23:14:08 +00001734 def ChromiumSyncErrorOpHandler(self):
1735 test_name = "/chromiumsync/error"
1736 if not self._ShouldHandleRequest(test_name):
1737 return False
1738 result, raw_reply = self.server._sync_handler.HandleSetInducedError(
1739 self.path)
1740 self.send_response(result)
1741 self.send_header('Content-Type', 'text/html')
1742 self.send_header('Content-Length', len(raw_reply))
1743 self.end_headers()
1744 self.wfile.write(raw_reply)
1745 return True;
1746
zea@chromium.org1aa5e632011-08-25 22:21:02 +00001747 def ChromiumSyncSyncTabsOpHandler(self):
1748 test_name = "/chromiumsync/synctabs"
1749 if not self._ShouldHandleRequest(test_name):
1750 return False
1751 result, raw_reply = self.server._sync_handler.HandleSetSyncTabs()
1752 self.send_response(result)
1753 self.send_header('Content-Type', 'text/html')
1754 self.send_header('Content-Length', len(raw_reply))
1755 self.end_headers()
1756 self.wfile.write(raw_reply)
1757 return True;
1758
akalin@chromium.org154bb132010-11-12 02:20:27 +00001759
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001760def MakeDataDir():
1761 if options.data_dir:
1762 if not os.path.isdir(options.data_dir):
1763 print 'specified data dir not found: ' + options.data_dir + ' exiting...'
1764 return None
1765 my_data_dir = options.data_dir
1766 else:
1767 # Create the default path to our data dir, relative to the exe dir.
1768 my_data_dir = os.path.dirname(sys.argv[0])
1769 my_data_dir = os.path.join(my_data_dir, "..", "..", "..", "..",
timurrrr@chromium.orgb9006f52010-04-30 14:50:58 +00001770 "test", "data")
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001771
1772 #TODO(ibrar): Must use Find* funtion defined in google\tools
1773 #i.e my_data_dir = FindUpward(my_data_dir, "test", "data")
1774
1775 return my_data_dir
1776
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +00001777
1778class TCPEchoHandler(SocketServer.BaseRequestHandler):
1779 """The RequestHandler class for TCP echo server.
1780
1781 It is instantiated once per connection to the server, and overrides the
1782 handle() method to implement communication to the client.
1783 """
1784
1785 def handle(self):
rtenneti@chromium.org922a8222011-08-16 03:30:45 +00001786 """Handles the request from the client and constructs a response."""
1787
1788 data = self.request.recv(65536).strip()
1789 # Verify the "echo request" message received from the client. Send back
1790 # "echo response" message if "echo request" message is valid.
1791 try:
1792 return_data = echo_message.GetEchoResponseData(data)
1793 if not return_data:
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +00001794 return
rtenneti@chromium.org922a8222011-08-16 03:30:45 +00001795 except ValueError:
1796 return
1797
1798 self.request.send(return_data)
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +00001799
1800
1801class UDPEchoHandler(SocketServer.BaseRequestHandler):
1802 """The RequestHandler class for UDP echo server.
1803
1804 It is instantiated once per connection to the server, and overrides the
1805 handle() method to implement communication to the client.
1806 """
1807
1808 def handle(self):
rtenneti@chromium.org922a8222011-08-16 03:30:45 +00001809 """Handles the request from the client and constructs a response."""
1810
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +00001811 data = self.request[0].strip()
1812 socket = self.request[1]
rtenneti@chromium.org922a8222011-08-16 03:30:45 +00001813 # Verify the "echo request" message received from the client. Send back
1814 # "echo response" message if "echo request" message is valid.
1815 try:
1816 return_data = echo_message.GetEchoResponseData(data)
1817 if not return_data:
1818 return
1819 except ValueError:
1820 return
1821 socket.sendto(return_data, self.client_address)
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +00001822
1823
phajdan.jr@chromium.orgbf74e2b2010-08-17 20:07:11 +00001824class FileMultiplexer:
1825 def __init__(self, fd1, fd2) :
1826 self.__fd1 = fd1
1827 self.__fd2 = fd2
1828
1829 def __del__(self) :
1830 if self.__fd1 != sys.stdout and self.__fd1 != sys.stderr:
1831 self.__fd1.close()
1832 if self.__fd2 != sys.stdout and self.__fd2 != sys.stderr:
1833 self.__fd2.close()
1834
1835 def write(self, text) :
1836 self.__fd1.write(text)
1837 self.__fd2.write(text)
1838
1839 def flush(self) :
1840 self.__fd1.flush()
1841 self.__fd2.flush()
1842
initial.commit94958cf2008-07-26 22:42:52 +00001843def main(options, args):
initial.commit94958cf2008-07-26 22:42:52 +00001844 logfile = open('testserver.log', 'w')
phajdan.jr@chromium.orgbf74e2b2010-08-17 20:07:11 +00001845 sys.stderr = FileMultiplexer(sys.stderr, logfile)
rsimha@chromium.orge77acad2011-02-01 19:56:46 +00001846 if options.log_to_console:
1847 sys.stdout = FileMultiplexer(sys.stdout, logfile)
1848 else:
1849 sys.stdout = logfile
initial.commit94958cf2008-07-26 22:42:52 +00001850
1851 port = options.port
1852
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +00001853 server_data = {}
1854
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001855 if options.server_type == SERVER_HTTP:
1856 if options.cert:
1857 # let's make sure the cert file exists.
1858 if not os.path.isfile(options.cert):
rsleevi@chromium.orgb2ecdab2010-08-21 04:02:44 +00001859 print 'specified server cert file not found: ' + options.cert + \
1860 ' exiting...'
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001861 return
rsleevi@chromium.orgb2ecdab2010-08-21 04:02:44 +00001862 for ca_cert in options.ssl_client_ca:
1863 if not os.path.isfile(ca_cert):
1864 print 'specified trusted client CA file not found: ' + ca_cert + \
1865 ' exiting...'
1866 return
davidben@chromium.org31282a12010-08-07 01:10:02 +00001867 server = HTTPSServer(('127.0.0.1', port), TestPageHandler, options.cert,
rsleevi@chromium.org2124c812010-10-28 11:57:36 +00001868 options.ssl_client_auth, options.ssl_client_ca,
agl@chromium.orgf9e66792011-12-12 22:22:19 +00001869 options.ssl_bulk_cipher, options.record_resume)
cbentzel@chromium.org0787bc72010-11-11 20:31:31 +00001870 print 'HTTPS server started on port %d...' % server.server_port
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001871 else:
phajdan.jr@chromium.orgfa49b5f2010-07-23 20:38:56 +00001872 server = StoppableHTTPServer(('127.0.0.1', port), TestPageHandler)
cbentzel@chromium.org0787bc72010-11-11 20:31:31 +00001873 print 'HTTP server started on port %d...' % server.server_port
erikkay@google.com70397b62008-12-30 21:49:21 +00001874
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001875 server.data_dir = MakeDataDir()
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +00001876 server.file_root_url = options.file_root_url
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +00001877 server_data['port'] = server.server_port
mnissler@chromium.org7c939802010-11-11 08:47:14 +00001878 server._device_management_handler = None
mnissler@chromium.orgcfe39282011-04-11 12:13:05 +00001879 server.policy_keys = options.policy_keys
1880 server.policy_user = options.policy_user
akalin@chromium.org154bb132010-11-12 02:20:27 +00001881 elif options.server_type == SERVER_SYNC:
1882 server = SyncHTTPServer(('127.0.0.1', port), SyncPageHandler)
1883 print 'Sync HTTP server started on port %d...' % server.server_port
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +00001884 print 'Sync XMPP server started on port %d...' % server.xmpp_port
1885 server_data['port'] = server.server_port
1886 server_data['xmpp_port'] = server.xmpp_port
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +00001887 elif options.server_type == SERVER_TCP_ECHO:
rtenneti@chromium.org922a8222011-08-16 03:30:45 +00001888 # Used for generating the key (randomly) that encodes the "echo request"
1889 # message.
1890 random.seed()
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +00001891 server = TCPEchoServer(('127.0.0.1', port), TCPEchoHandler)
1892 print 'Echo TCP server started on port %d...' % server.server_port
1893 server_data['port'] = server.server_port
1894 elif options.server_type == SERVER_UDP_ECHO:
rtenneti@chromium.org922a8222011-08-16 03:30:45 +00001895 # Used for generating the key (randomly) that encodes the "echo request"
1896 # message.
1897 random.seed()
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +00001898 server = UDPEchoServer(('127.0.0.1', port), UDPEchoHandler)
1899 print 'Echo UDP server started on port %d...' % server.server_port
1900 server_data['port'] = server.server_port
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001901 # means FTP Server
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +00001902 else:
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001903 my_data_dir = MakeDataDir()
1904
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001905 # Instantiate a dummy authorizer for managing 'virtual' users
1906 authorizer = pyftpdlib.ftpserver.DummyAuthorizer()
1907
1908 # Define a new user having full r/w permissions and a read-only
1909 # anonymous user
1910 authorizer.add_user('chrome', 'chrome', my_data_dir, perm='elradfmw')
1911
1912 authorizer.add_anonymous(my_data_dir)
1913
1914 # Instantiate FTP handler class
1915 ftp_handler = pyftpdlib.ftpserver.FTPHandler
1916 ftp_handler.authorizer = authorizer
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001917
1918 # Define a customized banner (string returned when client connects)
maruel@google.come250a9b2009-03-10 17:39:46 +00001919 ftp_handler.banner = ("pyftpdlib %s based ftpd ready." %
1920 pyftpdlib.ftpserver.__ver__)
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001921
1922 # Instantiate FTP server class and listen to 127.0.0.1:port
1923 address = ('127.0.0.1', port)
1924 server = pyftpdlib.ftpserver.FTPServer(address, ftp_handler)
akalin@chromium.org4e4f3c92010-11-27 04:04:52 +00001925 server_data['port'] = server.socket.getsockname()[1]
1926 print 'FTP server started on port %d...' % server_data['port']
initial.commit94958cf2008-07-26 22:42:52 +00001927
davidben@chromium.org06fcf202010-09-22 18:15:23 +00001928 # Notify the parent that we've started. (BaseServer subclasses
1929 # bind their sockets on construction.)
1930 if options.startup_pipe is not None:
dpranke@chromium.org70049b72011-10-14 00:38:18 +00001931 server_data_json = json.dumps(server_data)
akalin@chromium.orgf8479c62010-11-27 01:52:52 +00001932 server_data_len = len(server_data_json)
1933 print 'sending server_data: %s (%d bytes)' % (
1934 server_data_json, server_data_len)
davidben@chromium.org06fcf202010-09-22 18:15:23 +00001935 if sys.platform == 'win32':
1936 fd = msvcrt.open_osfhandle(options.startup_pipe, 0)
1937 else:
1938 fd = options.startup_pipe
1939 startup_pipe = os.fdopen(fd, "w")
akalin@chromium.orgf8479c62010-11-27 01:52:52 +00001940 # First write the data length as an unsigned 4-byte value. This
1941 # is _not_ using network byte ordering since the other end of the
1942 # pipe is on the same machine.
1943 startup_pipe.write(struct.pack('=L', server_data_len))
1944 startup_pipe.write(server_data_json)
davidben@chromium.org06fcf202010-09-22 18:15:23 +00001945 startup_pipe.close()
1946
initial.commit94958cf2008-07-26 22:42:52 +00001947 try:
1948 server.serve_forever()
1949 except KeyboardInterrupt:
1950 print 'shutting down server'
1951 server.stop = True
1952
1953if __name__ == '__main__':
1954 option_parser = optparse.OptionParser()
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001955 option_parser.add_option("-f", '--ftp', action='store_const',
1956 const=SERVER_FTP, default=SERVER_HTTP,
1957 dest='server_type',
akalin@chromium.org154bb132010-11-12 02:20:27 +00001958 help='start up an FTP server.')
1959 option_parser.add_option('', '--sync', action='store_const',
1960 const=SERVER_SYNC, default=SERVER_HTTP,
1961 dest='server_type',
1962 help='start up a sync server.')
rtenneti@chromium.orgfc70e5e2011-06-09 05:11:41 +00001963 option_parser.add_option('', '--tcp-echo', action='store_const',
1964 const=SERVER_TCP_ECHO, default=SERVER_HTTP,
1965 dest='server_type',
1966 help='start up a tcp echo server.')
1967 option_parser.add_option('', '--udp-echo', action='store_const',
1968 const=SERVER_UDP_ECHO, default=SERVER_HTTP,
1969 dest='server_type',
1970 help='start up a udp echo server.')
rsimha@chromium.orge77acad2011-02-01 19:56:46 +00001971 option_parser.add_option('', '--log-to-console', action='store_const',
1972 const=True, default=False,
1973 dest='log_to_console',
1974 help='Enables or disables sys.stdout logging to '
1975 'the console.')
cbentzel@chromium.org0787bc72010-11-11 20:31:31 +00001976 option_parser.add_option('', '--port', default='0', type='int',
1977 help='Port used by the server. If unspecified, the '
1978 'server will listen on an ephemeral port.')
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001979 option_parser.add_option('', '--data-dir', dest='data_dir',
robertshield@chromium.org5e231612010-01-20 18:23:53 +00001980 help='Directory from which to read the files.')
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001981 option_parser.add_option('', '--https', dest='cert',
initial.commit94958cf2008-07-26 22:42:52 +00001982 help='Specify that https should be used, specify '
1983 'the path to the cert containing the private key '
robertshield@chromium.org5e231612010-01-20 18:23:53 +00001984 'the server should use.')
agl@chromium.orgf9e66792011-12-12 22:22:19 +00001985 option_parser.add_option('', '--https-record-resume', dest='record_resume',
1986 const=True, default=False, action='store_const',
1987 help='Record resumption cache events rather than'
1988 ' resuming as normal. Allows the use of the'
1989 ' /ssl-session-cache request')
davidben@chromium.org31282a12010-08-07 01:10:02 +00001990 option_parser.add_option('', '--ssl-client-auth', action='store_true',
1991 help='Require SSL client auth on every connection.')
rsleevi@chromium.orgb2ecdab2010-08-21 04:02:44 +00001992 option_parser.add_option('', '--ssl-client-ca', action='append', default=[],
1993 help='Specify that the client certificate request '
rsleevi@chromium.org2124c812010-10-28 11:57:36 +00001994 'should include the CA named in the subject of '
1995 'the DER-encoded certificate contained in the '
1996 'specified file. This option may appear multiple '
1997 'times, indicating multiple CA names should be '
1998 'sent in the request.')
1999 option_parser.add_option('', '--ssl-bulk-cipher', action='append',
2000 help='Specify the bulk encryption algorithm(s)'
2001 'that will be accepted by the SSL server. Valid '
2002 'values are "aes256", "aes128", "3des", "rc4". If '
2003 'omitted, all algorithms will be used. This '
2004 'option may appear multiple times, indicating '
2005 'multiple algorithms should be enabled.');
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +00002006 option_parser.add_option('', '--file-root-url', default='/files/',
2007 help='Specify a root URL for files served.')
davidben@chromium.org06fcf202010-09-22 18:15:23 +00002008 option_parser.add_option('', '--startup-pipe', type='int',
2009 dest='startup_pipe',
2010 help='File handle of pipe to parent process')
mnissler@chromium.orgcfe39282011-04-11 12:13:05 +00002011 option_parser.add_option('', '--policy-key', action='append',
2012 dest='policy_keys',
2013 help='Specify a path to a PEM-encoded private key '
2014 'to use for policy signing. May be specified '
2015 'multiple times in order to load multipe keys into '
mnissler@chromium.org1655c712011-04-18 11:13:25 +00002016 'the server. If ther server has multiple keys, it '
2017 'will rotate through them in at each request a '
2018 'round-robin fashion. The server will generate a '
2019 'random key if none is specified on the command '
2020 'line.')
mnissler@chromium.orgcfe39282011-04-11 12:13:05 +00002021 option_parser.add_option('', '--policy-user', default='user@example.com',
2022 dest='policy_user',
2023 help='Specify the user name the server should '
2024 'report back to the client as the user owning the '
2025 'token used for making the policy request.')
initial.commit94958cf2008-07-26 22:42:52 +00002026 options, args = option_parser.parse_args()
2027
2028 sys.exit(main(options, args))