blob: 81506027568c03367c52337d9c730ebb3dabee78 [file] [log] [blame]
initial.commit94958cf2008-07-26 22:42:52 +00001#!/usr/bin/python2.4
license.botf3378c22008-08-24 00:55:55 +00002# Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
3# 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
6"""This is a simple HTTP server used for testing Chrome.
7
8It supports several test URLs, as specified by the handlers in TestPageHandler.
9It defaults to living on localhost:8888.
10It can use https if you specify the flag --https=CERT where CERT is the path
11to a pem file containing the certificate and private key that should be used.
12To shut it down properly, visit localhost:8888/kill.
13"""
14
15import base64
16import BaseHTTPServer
17import cgi
initial.commit94958cf2008-07-26 22:42:52 +000018import optparse
19import os
20import re
stoyan@chromium.org372692c2009-01-30 17:01:52 +000021import shutil
initial.commit94958cf2008-07-26 22:42:52 +000022import SocketServer
23import sys
24import time
25import tlslite
26import tlslite.api
erikkay@google.comd5182ff2009-01-08 20:45:27 +000027import pyftpdlib.ftpserver
28
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +000029try:
30 import hashlib
31 _new_md5 = hashlib.md5
32except ImportError:
33 import md5
34 _new_md5 = md5.new
35
maruel@chromium.org756cf982009-03-05 12:46:38 +000036SERVER_HTTP = 0
erikkay@google.comd5182ff2009-01-08 20:45:27 +000037SERVER_FTP = 1
initial.commit94958cf2008-07-26 22:42:52 +000038
39debug_output = sys.stderr
40def debug(str):
41 debug_output.write(str + "\n")
42 debug_output.flush()
43
44class StoppableHTTPServer(BaseHTTPServer.HTTPServer):
45 """This is a specialization of of BaseHTTPServer to allow it
46 to be exited cleanly (by setting its "stop" member to True)."""
47
48 def serve_forever(self):
49 self.stop = False
50 self.nonce = None
51 while not self.stop:
52 self.handle_request()
53 self.socket.close()
54
55class HTTPSServer(tlslite.api.TLSSocketServerMixIn, StoppableHTTPServer):
56 """This is a specialization of StoppableHTTPerver that add https support."""
57
58 def __init__(self, server_address, request_hander_class, cert_path):
59 s = open(cert_path).read()
60 x509 = tlslite.api.X509()
61 x509.parse(s)
62 self.cert_chain = tlslite.api.X509CertChain([x509])
63 s = open(cert_path).read()
64 self.private_key = tlslite.api.parsePEMKey(s, private=True)
65
66 self.session_cache = tlslite.api.SessionCache()
67 StoppableHTTPServer.__init__(self, server_address, request_hander_class)
68
69 def handshake(self, tlsConnection):
70 """Creates the SSL connection."""
71 try:
72 tlsConnection.handshakeServer(certChain=self.cert_chain,
73 privateKey=self.private_key,
74 sessionCache=self.session_cache)
75 tlsConnection.ignoreAbruptClose = True
76 return True
77 except tlslite.api.TLSError, error:
78 print "Handshake failure:", str(error)
79 return False
80
phajdan.jr@chromium.org599efb82009-08-14 22:37:35 +000081class ForkingHTTPServer(SocketServer.ForkingMixIn, StoppableHTTPServer):
82 """This is a specialization of of StoppableHTTPServer which serves each
83 request in a separate process"""
84 pass
85
86class ForkingHTTPSServer(SocketServer.ForkingMixIn, HTTPSServer):
87 """This is a specialization of of HTTPSServer which serves each
88 request in a separate process"""
89 pass
90
initial.commit94958cf2008-07-26 22:42:52 +000091class TestPageHandler(BaseHTTPServer.BaseHTTPRequestHandler):
92
93 def __init__(self, request, client_address, socket_server):
wtc@chromium.org743d77b2009-02-11 02:48:15 +000094 self._connect_handlers = [
95 self.RedirectConnectHandler,
wtc@chromium.orgb86c7f92009-02-14 01:45:08 +000096 self.ServerAuthConnectHandler,
wtc@chromium.org743d77b2009-02-11 02:48:15 +000097 self.DefaultConnectResponseHandler]
initial.commit94958cf2008-07-26 22:42:52 +000098 self._get_handlers = [
99 self.KillHandler,
100 self.NoCacheMaxAgeTimeHandler,
101 self.NoCacheTimeHandler,
102 self.CacheTimeHandler,
103 self.CacheExpiresHandler,
104 self.CacheProxyRevalidateHandler,
105 self.CachePrivateHandler,
106 self.CachePublicHandler,
107 self.CacheSMaxAgeHandler,
108 self.CacheMustRevalidateHandler,
109 self.CacheMustRevalidateMaxAgeHandler,
110 self.CacheNoStoreHandler,
111 self.CacheNoStoreMaxAgeHandler,
112 self.CacheNoTransformHandler,
113 self.DownloadHandler,
114 self.DownloadFinishHandler,
115 self.EchoHeader,
ananta@chromium.org219b2062009-10-23 16:09:41 +0000116 self.EchoHeaderOverride,
ericroman@google.coma47622b2008-11-15 04:36:51 +0000117 self.EchoAllHandler,
initial.commit94958cf2008-07-26 22:42:52 +0000118 self.FileHandler,
119 self.RealFileWithCommonHeaderHandler,
120 self.RealBZ2FileWithCommonHeaderHandler,
levin@chromium.orgf7ee2e42009-08-26 02:33:46 +0000121 self.SetCookieHandler,
initial.commit94958cf2008-07-26 22:42:52 +0000122 self.AuthBasicHandler,
123 self.AuthDigestHandler,
124 self.SlowServerHandler,
125 self.ContentTypeHandler,
126 self.ServerRedirectHandler,
127 self.ClientRedirectHandler,
128 self.DefaultResponseHandler]
129 self._post_handlers = [
stoyan@chromium.org372692c2009-01-30 17:01:52 +0000130 self.WriteFile,
initial.commit94958cf2008-07-26 22:42:52 +0000131 self.EchoTitleHandler,
132 self.EchoAllHandler,
133 self.EchoHandler] + self._get_handlers
ananta@chromium.org56d146f2010-01-11 19:03:01 +0000134 self._put_handlers = [
135 self.WriteFile,
136 self.EchoTitleHandler,
137 self.EchoAllHandler,
138 self.EchoHandler] + self._get_handlers
initial.commit94958cf2008-07-26 22:42:52 +0000139
maruel@google.come250a9b2009-03-10 17:39:46 +0000140 self._mime_types = {
141 'gif': 'image/gif',
142 'jpeg' : 'image/jpeg',
finnur@chromium.org88e84c32009-10-02 17:59:55 +0000143 'jpg' : 'image/jpeg',
144 'xml' : 'text/xml'
maruel@google.come250a9b2009-03-10 17:39:46 +0000145 }
initial.commit94958cf2008-07-26 22:42:52 +0000146 self._default_mime_type = 'text/html'
147
maruel@google.come250a9b2009-03-10 17:39:46 +0000148 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, request,
149 client_address,
150 socket_server)
initial.commit94958cf2008-07-26 22:42:52 +0000151
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000152 def _ShouldHandleRequest(self, handler_name):
153 """Determines if the path can be handled by the handler.
154
155 We consider a handler valid if the path begins with the
156 handler name. It can optionally be followed by "?*", "/*".
157 """
158
159 pattern = re.compile('%s($|\?|/).*' % handler_name)
160 return pattern.match(self.path)
161
initial.commit94958cf2008-07-26 22:42:52 +0000162 def GetMIMETypeFromName(self, file_name):
163 """Returns the mime type for the specified file_name. So far it only looks
164 at the file extension."""
165
166 (shortname, extension) = os.path.splitext(file_name)
167 if len(extension) == 0:
168 # no extension.
169 return self._default_mime_type
170
ericroman@google.comc17ca532009-05-07 03:51:05 +0000171 # extension starts with a dot, so we need to remove it
172 return self._mime_types.get(extension[1:], self._default_mime_type)
initial.commit94958cf2008-07-26 22:42:52 +0000173
174 def KillHandler(self):
175 """This request handler kills the server, for use when we're done"
176 with the a particular test."""
177
178 if (self.path.find("kill") < 0):
179 return False
180
181 self.send_response(200)
182 self.send_header('Content-type', 'text/html')
183 self.send_header('Cache-Control', 'max-age=0')
184 self.end_headers()
185 self.wfile.write("Time to die")
186 self.server.stop = True
187
188 return True
189
190 def NoCacheMaxAgeTimeHandler(self):
191 """This request handler yields a page with the title set to the current
192 system time, and no caching requested."""
193
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000194 if not self._ShouldHandleRequest("/nocachetime/maxage"):
initial.commit94958cf2008-07-26 22:42:52 +0000195 return False
196
197 self.send_response(200)
198 self.send_header('Cache-Control', 'max-age=0')
199 self.send_header('Content-type', 'text/html')
200 self.end_headers()
201
maruel@google.come250a9b2009-03-10 17:39:46 +0000202 self.wfile.write('<html><head><title>%s</title></head></html>' %
203 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000204
205 return True
206
207 def NoCacheTimeHandler(self):
208 """This request handler yields a page with the title set to the current
209 system time, and no caching requested."""
210
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000211 if not self._ShouldHandleRequest("/nocachetime"):
initial.commit94958cf2008-07-26 22:42:52 +0000212 return False
213
214 self.send_response(200)
215 self.send_header('Cache-Control', 'no-cache')
216 self.send_header('Content-type', 'text/html')
217 self.end_headers()
218
maruel@google.come250a9b2009-03-10 17:39:46 +0000219 self.wfile.write('<html><head><title>%s</title></head></html>' %
220 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000221
222 return True
223
224 def CacheTimeHandler(self):
225 """This request handler yields a page with the title set to the current
226 system time, and allows caching for one minute."""
227
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000228 if not self._ShouldHandleRequest("/cachetime"):
initial.commit94958cf2008-07-26 22:42:52 +0000229 return False
230
231 self.send_response(200)
232 self.send_header('Cache-Control', 'max-age=60')
233 self.send_header('Content-type', 'text/html')
234 self.end_headers()
235
maruel@google.come250a9b2009-03-10 17:39:46 +0000236 self.wfile.write('<html><head><title>%s</title></head></html>' %
237 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000238
239 return True
240
241 def CacheExpiresHandler(self):
242 """This request handler yields a page with the title set to the current
243 system time, and set the page to expire on 1 Jan 2099."""
244
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000245 if not self._ShouldHandleRequest("/cache/expires"):
initial.commit94958cf2008-07-26 22:42:52 +0000246 return False
247
248 self.send_response(200)
249 self.send_header('Expires', 'Thu, 1 Jan 2099 00:00:00 GMT')
250 self.send_header('Content-type', 'text/html')
251 self.end_headers()
252
maruel@google.come250a9b2009-03-10 17:39:46 +0000253 self.wfile.write('<html><head><title>%s</title></head></html>' %
254 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000255
256 return True
257
258 def CacheProxyRevalidateHandler(self):
259 """This request handler yields a page with the title set to the current
260 system time, and allows caching for 60 seconds"""
261
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000262 if not self._ShouldHandleRequest("/cache/proxy-revalidate"):
initial.commit94958cf2008-07-26 22:42:52 +0000263 return False
264
265 self.send_response(200)
266 self.send_header('Content-type', 'text/html')
267 self.send_header('Cache-Control', 'max-age=60, proxy-revalidate')
268 self.end_headers()
269
maruel@google.come250a9b2009-03-10 17:39:46 +0000270 self.wfile.write('<html><head><title>%s</title></head></html>' %
271 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000272
273 return True
274
275 def CachePrivateHandler(self):
276 """This request handler yields a page with the title set to the current
277 system time, and allows caching for 5 seconds."""
278
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000279 if not self._ShouldHandleRequest("/cache/private"):
initial.commit94958cf2008-07-26 22:42:52 +0000280 return False
281
282 self.send_response(200)
283 self.send_header('Content-type', 'text/html')
huanr@chromium.orgab5be752009-05-23 02:58:44 +0000284 self.send_header('Cache-Control', 'max-age=3, private')
initial.commit94958cf2008-07-26 22:42:52 +0000285 self.end_headers()
286
maruel@google.come250a9b2009-03-10 17:39:46 +0000287 self.wfile.write('<html><head><title>%s</title></head></html>' %
288 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000289
290 return True
291
292 def CachePublicHandler(self):
293 """This request handler yields a page with the title set to the current
294 system time, and allows caching for 5 seconds."""
295
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000296 if not self._ShouldHandleRequest("/cache/public"):
initial.commit94958cf2008-07-26 22:42:52 +0000297 return False
298
299 self.send_response(200)
300 self.send_header('Content-type', 'text/html')
huanr@chromium.orgab5be752009-05-23 02:58:44 +0000301 self.send_header('Cache-Control', 'max-age=3, public')
initial.commit94958cf2008-07-26 22:42:52 +0000302 self.end_headers()
303
maruel@google.come250a9b2009-03-10 17:39:46 +0000304 self.wfile.write('<html><head><title>%s</title></head></html>' %
305 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000306
307 return True
308
309 def CacheSMaxAgeHandler(self):
310 """This request handler yields a page with the title set to the current
311 system time, and does not allow for caching."""
312
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000313 if not self._ShouldHandleRequest("/cache/s-maxage"):
initial.commit94958cf2008-07-26 22:42:52 +0000314 return False
315
316 self.send_response(200)
317 self.send_header('Content-type', 'text/html')
318 self.send_header('Cache-Control', 'public, s-maxage = 60, max-age = 0')
319 self.end_headers()
320
maruel@google.come250a9b2009-03-10 17:39:46 +0000321 self.wfile.write('<html><head><title>%s</title></head></html>' %
322 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000323
324 return True
325
326 def CacheMustRevalidateHandler(self):
327 """This request handler yields a page with the title set to the current
328 system time, and does not allow caching."""
329
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000330 if not self._ShouldHandleRequest("/cache/must-revalidate"):
initial.commit94958cf2008-07-26 22:42:52 +0000331 return False
332
333 self.send_response(200)
334 self.send_header('Content-type', 'text/html')
335 self.send_header('Cache-Control', 'must-revalidate')
336 self.end_headers()
337
maruel@google.come250a9b2009-03-10 17:39:46 +0000338 self.wfile.write('<html><head><title>%s</title></head></html>' %
339 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000340
341 return True
342
343 def CacheMustRevalidateMaxAgeHandler(self):
344 """This request handler yields a page with the title set to the current
345 system time, and does not allow caching event though max-age of 60
346 seconds is specified."""
347
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000348 if not self._ShouldHandleRequest("/cache/must-revalidate/max-age"):
initial.commit94958cf2008-07-26 22:42:52 +0000349 return False
350
351 self.send_response(200)
352 self.send_header('Content-type', 'text/html')
353 self.send_header('Cache-Control', 'max-age=60, must-revalidate')
354 self.end_headers()
355
maruel@google.come250a9b2009-03-10 17:39:46 +0000356 self.wfile.write('<html><head><title>%s</title></head></html>' %
357 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000358
359 return True
360
initial.commit94958cf2008-07-26 22:42:52 +0000361 def CacheNoStoreHandler(self):
362 """This request handler yields a page with the title set to the current
363 system time, and does not allow the page to be stored."""
364
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000365 if not self._ShouldHandleRequest("/cache/no-store"):
initial.commit94958cf2008-07-26 22:42:52 +0000366 return False
367
368 self.send_response(200)
369 self.send_header('Content-type', 'text/html')
370 self.send_header('Cache-Control', 'no-store')
371 self.end_headers()
372
maruel@google.come250a9b2009-03-10 17:39:46 +0000373 self.wfile.write('<html><head><title>%s</title></head></html>' %
374 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000375
376 return True
377
378 def CacheNoStoreMaxAgeHandler(self):
379 """This request handler yields a page with the title set to the current
380 system time, and does not allow the page to be stored even though max-age
381 of 60 seconds is specified."""
382
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000383 if not self._ShouldHandleRequest("/cache/no-store/max-age"):
initial.commit94958cf2008-07-26 22:42:52 +0000384 return False
385
386 self.send_response(200)
387 self.send_header('Content-type', 'text/html')
388 self.send_header('Cache-Control', 'max-age=60, no-store')
389 self.end_headers()
390
maruel@google.come250a9b2009-03-10 17:39:46 +0000391 self.wfile.write('<html><head><title>%s</title></head></html>' %
392 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000393
394 return True
395
396
397 def CacheNoTransformHandler(self):
398 """This request handler yields a page with the title set to the current
399 system time, and does not allow the content to transformed during
400 user-agent caching"""
401
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000402 if not self._ShouldHandleRequest("/cache/no-transform"):
initial.commit94958cf2008-07-26 22:42:52 +0000403 return False
404
405 self.send_response(200)
406 self.send_header('Content-type', 'text/html')
407 self.send_header('Cache-Control', 'no-transform')
408 self.end_headers()
409
maruel@google.come250a9b2009-03-10 17:39:46 +0000410 self.wfile.write('<html><head><title>%s</title></head></html>' %
411 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000412
413 return True
414
415 def EchoHeader(self):
416 """This handler echoes back the value of a specific request header."""
ananta@chromium.org219b2062009-10-23 16:09:41 +0000417 """The only difference between this function and the EchoHeaderOverride"""
418 """function is in the parameter being passed to the helper function"""
419 return self.EchoHeaderHelper("/echoheader")
initial.commit94958cf2008-07-26 22:42:52 +0000420
ananta@chromium.org219b2062009-10-23 16:09:41 +0000421 def EchoHeaderOverride(self):
422 """This handler echoes back the value of a specific request header."""
423 """The UrlRequest unit tests also execute for ChromeFrame which uses"""
424 """IE to issue HTTP requests using the host network stack."""
425 """The Accept and Charset tests which expect the server to echo back"""
426 """the corresponding headers fail here as IE returns cached responses"""
427 """The EchoHeaderOverride parameter is an easy way to ensure that IE"""
428 """treats this request as a new request and does not cache it."""
429 return self.EchoHeaderHelper("/echoheaderoverride")
430
431 def EchoHeaderHelper(self, echo_header):
432 """This function echoes back the value of the request header passed in."""
433 if not self._ShouldHandleRequest(echo_header):
initial.commit94958cf2008-07-26 22:42:52 +0000434 return False
435
436 query_char = self.path.find('?')
437 if query_char != -1:
438 header_name = self.path[query_char+1:]
439
440 self.send_response(200)
441 self.send_header('Content-type', 'text/plain')
442 self.send_header('Cache-control', 'max-age=60000')
443 # insert a vary header to properly indicate that the cachability of this
444 # request is subject to value of the request header being echoed.
445 if len(header_name) > 0:
446 self.send_header('Vary', header_name)
447 self.end_headers()
448
449 if len(header_name) > 0:
450 self.wfile.write(self.headers.getheader(header_name))
451
452 return True
453
454 def EchoHandler(self):
455 """This handler just echoes back the payload of the request, for testing
456 form submission."""
457
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000458 if not self._ShouldHandleRequest("/echo"):
initial.commit94958cf2008-07-26 22:42:52 +0000459 return False
460
461 self.send_response(200)
462 self.send_header('Content-type', 'text/html')
463 self.end_headers()
464 length = int(self.headers.getheader('content-length'))
465 request = self.rfile.read(length)
466 self.wfile.write(request)
467 return True
468
stoyan@chromium.org372692c2009-01-30 17:01:52 +0000469 def WriteFile(self):
ananta@chromium.org56d146f2010-01-11 19:03:01 +0000470 """This is handler dumps the content of POST/PUT request to a disk file
471 into the data_dir/dump. Sub-directories are not supported."""
maruel@chromium.org756cf982009-03-05 12:46:38 +0000472
stoyan@chromium.org372692c2009-01-30 17:01:52 +0000473 prefix='/writefile/'
474 if not self.path.startswith(prefix):
475 return False
maruel@chromium.org756cf982009-03-05 12:46:38 +0000476
stoyan@chromium.org372692c2009-01-30 17:01:52 +0000477 file_name = self.path[len(prefix):]
478
479 # do not allow fancy chars in file name
480 re.sub('[^a-zA-Z0-9_.-]+', '', file_name)
481 if len(file_name) and file_name[0] != '.':
482 path = os.path.join(self.server.data_dir, 'dump', file_name);
483 length = int(self.headers.getheader('content-length'))
484 request = self.rfile.read(length)
485 f = open(path, "wb")
486 f.write(request);
487 f.close()
maruel@chromium.org756cf982009-03-05 12:46:38 +0000488
stoyan@chromium.org372692c2009-01-30 17:01:52 +0000489 self.send_response(200)
490 self.send_header('Content-type', 'text/html')
491 self.end_headers()
492 self.wfile.write('<html>%s</html>' % file_name)
493 return True
maruel@chromium.org756cf982009-03-05 12:46:38 +0000494
initial.commit94958cf2008-07-26 22:42:52 +0000495 def EchoTitleHandler(self):
496 """This handler is like Echo, but sets the page title to the request."""
497
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000498 if not self._ShouldHandleRequest("/echotitle"):
initial.commit94958cf2008-07-26 22:42:52 +0000499 return False
500
501 self.send_response(200)
502 self.send_header('Content-type', 'text/html')
503 self.end_headers()
504 length = int(self.headers.getheader('content-length'))
505 request = self.rfile.read(length)
506 self.wfile.write('<html><head><title>')
507 self.wfile.write(request)
508 self.wfile.write('</title></head></html>')
509 return True
510
511 def EchoAllHandler(self):
512 """This handler yields a (more) human-readable page listing information
513 about the request header & contents."""
514
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000515 if not self._ShouldHandleRequest("/echoall"):
initial.commit94958cf2008-07-26 22:42:52 +0000516 return False
517
518 self.send_response(200)
519 self.send_header('Content-type', 'text/html')
520 self.end_headers()
521 self.wfile.write('<html><head><style>'
522 'pre { border: 1px solid black; margin: 5px; padding: 5px }'
523 '</style></head><body>'
524 '<div style="float: right">'
525 '<a href="http://localhost:8888/echo">back to referring page</a></div>'
526 '<h1>Request Body:</h1><pre>')
initial.commit94958cf2008-07-26 22:42:52 +0000527
ananta@chromium.org56d146f2010-01-11 19:03:01 +0000528 if self.command == 'POST' or self.command == 'PUT':
ericroman@google.coma47622b2008-11-15 04:36:51 +0000529 length = int(self.headers.getheader('content-length'))
530 qs = self.rfile.read(length)
531 params = cgi.parse_qs(qs, keep_blank_values=1)
532
533 for param in params:
534 self.wfile.write('%s=%s\n' % (param, params[param][0]))
initial.commit94958cf2008-07-26 22:42:52 +0000535
536 self.wfile.write('</pre>')
537
538 self.wfile.write('<h1>Request Headers:</h1><pre>%s</pre>' % self.headers)
539
540 self.wfile.write('</body></html>')
541 return True
542
543 def DownloadHandler(self):
544 """This handler sends a downloadable file with or without reporting
545 the size (6K)."""
546
547 if self.path.startswith("/download-unknown-size"):
548 send_length = False
549 elif self.path.startswith("/download-known-size"):
550 send_length = True
551 else:
552 return False
553
554 #
555 # The test which uses this functionality is attempting to send
556 # small chunks of data to the client. Use a fairly large buffer
557 # so that we'll fill chrome's IO buffer enough to force it to
558 # actually write the data.
559 # See also the comments in the client-side of this test in
560 # download_uitest.cc
561 #
562 size_chunk1 = 35*1024
563 size_chunk2 = 10*1024
564
565 self.send_response(200)
566 self.send_header('Content-type', 'application/octet-stream')
567 self.send_header('Cache-Control', 'max-age=0')
568 if send_length:
569 self.send_header('Content-Length', size_chunk1 + size_chunk2)
570 self.end_headers()
571
572 # First chunk of data:
573 self.wfile.write("*" * size_chunk1)
574 self.wfile.flush()
575
576 # handle requests until one of them clears this flag.
577 self.server.waitForDownload = True
578 while self.server.waitForDownload:
579 self.server.handle_request()
580
581 # Second chunk of data:
582 self.wfile.write("*" * size_chunk2)
583 return True
584
585 def DownloadFinishHandler(self):
586 """This handler just tells the server to finish the current download."""
587
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000588 if not self._ShouldHandleRequest("/download-finish"):
initial.commit94958cf2008-07-26 22:42:52 +0000589 return False
590
591 self.server.waitForDownload = False
592 self.send_response(200)
593 self.send_header('Content-type', 'text/html')
594 self.send_header('Cache-Control', 'max-age=0')
595 self.end_headers()
596 return True
597
598 def FileHandler(self):
599 """This handler sends the contents of the requested file. Wow, it's like
600 a real webserver!"""
601
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +0000602 prefix = self.server.file_root_url
initial.commit94958cf2008-07-26 22:42:52 +0000603 if not self.path.startswith(prefix):
604 return False
605
darin@chromium.orgc25e5702009-07-23 19:10:23 +0000606 # Consume a request body if present.
ananta@chromium.org56d146f2010-01-11 19:03:01 +0000607 if self.command == 'POST' or self.command == 'PUT' :
darin@chromium.orgc25e5702009-07-23 19:10:23 +0000608 self.rfile.read(int(self.headers.getheader('content-length')))
609
initial.commit94958cf2008-07-26 22:42:52 +0000610 file = self.path[len(prefix):]
finnur@chromium.org88e84c32009-10-02 17:59:55 +0000611 if file.find('?') > -1:
612 # Ignore the query parameters entirely.
613 url, querystring = file.split('?')
614 else:
615 url = file
616 entries = url.split('/')
initial.commit94958cf2008-07-26 22:42:52 +0000617 path = os.path.join(self.server.data_dir, *entries)
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +0000618 if os.path.isdir(path):
619 path = os.path.join(path, 'index.html')
initial.commit94958cf2008-07-26 22:42:52 +0000620
621 if not os.path.isfile(path):
622 print "File not found " + file + " full path:" + path
623 self.send_error(404)
624 return True
625
626 f = open(path, "rb")
627 data = f.read()
628 f.close()
629
630 # If file.mock-http-headers exists, it contains the headers we
631 # should send. Read them in and parse them.
632 headers_path = path + '.mock-http-headers'
633 if os.path.isfile(headers_path):
634 f = open(headers_path, "r")
635
636 # "HTTP/1.1 200 OK"
637 response = f.readline()
638 status_code = re.findall('HTTP/\d+.\d+ (\d+)', response)[0]
639 self.send_response(int(status_code))
640
641 for line in f:
642 # "name: value"
643 name, value = re.findall('(\S+):\s*(.*)', line)[0]
644 self.send_header(name, value)
645 f.close()
646 else:
647 # Could be more generic once we support mime-type sniffing, but for
648 # now we need to set it explicitly.
649 self.send_response(200)
650 self.send_header('Content-type', self.GetMIMETypeFromName(file))
651 self.send_header('Content-Length', len(data))
652 self.end_headers()
653
654 self.wfile.write(data)
655
656 return True
657
658 def RealFileWithCommonHeaderHandler(self):
659 """This handler sends the contents of the requested file without the pseudo
660 http head!"""
661
662 prefix='/realfiles/'
663 if not self.path.startswith(prefix):
664 return False
665
666 file = self.path[len(prefix):]
667 path = os.path.join(self.server.data_dir, file)
668
669 try:
670 f = open(path, "rb")
671 data = f.read()
672 f.close()
673
674 # just simply set the MIME as octal stream
675 self.send_response(200)
676 self.send_header('Content-type', 'application/octet-stream')
677 self.end_headers()
678
679 self.wfile.write(data)
680 except:
681 self.send_error(404)
682
683 return True
684
685 def RealBZ2FileWithCommonHeaderHandler(self):
686 """This handler sends the bzip2 contents of the requested file with
687 corresponding Content-Encoding field in http head!"""
688
689 prefix='/realbz2files/'
690 if not self.path.startswith(prefix):
691 return False
692
693 parts = self.path.split('?')
694 file = parts[0][len(prefix):]
695 path = os.path.join(self.server.data_dir, file) + '.bz2'
696
697 if len(parts) > 1:
698 options = parts[1]
699 else:
700 options = ''
701
702 try:
703 self.send_response(200)
704 accept_encoding = self.headers.get("Accept-Encoding")
705 if accept_encoding.find("bzip2") != -1:
706 f = open(path, "rb")
707 data = f.read()
708 f.close()
709 self.send_header('Content-Encoding', 'bzip2')
710 self.send_header('Content-type', 'application/x-bzip2')
711 self.end_headers()
712 if options == 'incremental-header':
713 self.wfile.write(data[:1])
714 self.wfile.flush()
715 time.sleep(1.0)
716 self.wfile.write(data[1:])
717 else:
718 self.wfile.write(data)
719 else:
720 """client do not support bzip2 format, send pseudo content
721 """
722 self.send_header('Content-type', 'text/html; charset=ISO-8859-1')
723 self.end_headers()
724 self.wfile.write("you do not support bzip2 encoding")
725 except:
726 self.send_error(404)
727
728 return True
729
levin@chromium.orgf7ee2e42009-08-26 02:33:46 +0000730 def SetCookieHandler(self):
731 """This handler just sets a cookie, for testing cookie handling."""
732
733 if not self._ShouldHandleRequest("/set-cookie"):
734 return False
735
736 query_char = self.path.find('?')
737 if query_char != -1:
738 cookie_values = self.path[query_char + 1:].split('&')
739 else:
740 cookie_values = ("",)
741 self.send_response(200)
742 self.send_header('Content-type', 'text/html')
743 for cookie_value in cookie_values:
744 self.send_header('Set-Cookie', '%s' % cookie_value)
745 self.end_headers()
746 for cookie_value in cookie_values:
747 self.wfile.write('%s' % cookie_value)
748 return True
749
initial.commit94958cf2008-07-26 22:42:52 +0000750 def AuthBasicHandler(self):
751 """This handler tests 'Basic' authentication. It just sends a page with
752 title 'user/pass' if you succeed."""
753
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000754 if not self._ShouldHandleRequest("/auth-basic"):
initial.commit94958cf2008-07-26 22:42:52 +0000755 return False
756
757 username = userpass = password = b64str = ""
758
ericroman@google.com239b4d82009-03-27 04:00:22 +0000759 set_cookie_if_challenged = self.path.find('?set-cookie-if-challenged') > 0
760
initial.commit94958cf2008-07-26 22:42:52 +0000761 auth = self.headers.getheader('authorization')
762 try:
763 if not auth:
764 raise Exception('no auth')
765 b64str = re.findall(r'Basic (\S+)', auth)[0]
766 userpass = base64.b64decode(b64str)
767 username, password = re.findall(r'([^:]+):(\S+)', userpass)[0]
768 if password != 'secret':
769 raise Exception('wrong password')
770 except Exception, e:
771 # Authentication failed.
772 self.send_response(401)
773 self.send_header('WWW-Authenticate', 'Basic realm="testrealm"')
774 self.send_header('Content-type', 'text/html')
ericroman@google.com239b4d82009-03-27 04:00:22 +0000775 if set_cookie_if_challenged:
776 self.send_header('Set-Cookie', 'got_challenged=true')
initial.commit94958cf2008-07-26 22:42:52 +0000777 self.end_headers()
778 self.wfile.write('<html><head>')
779 self.wfile.write('<title>Denied: %s</title>' % e)
780 self.wfile.write('</head><body>')
781 self.wfile.write('auth=%s<p>' % auth)
782 self.wfile.write('b64str=%s<p>' % b64str)
783 self.wfile.write('username: %s<p>' % username)
784 self.wfile.write('userpass: %s<p>' % userpass)
785 self.wfile.write('password: %s<p>' % password)
786 self.wfile.write('You sent:<br>%s<p>' % self.headers)
787 self.wfile.write('</body></html>')
788 return True
789
790 # Authentication successful. (Return a cachable response to allow for
791 # testing cached pages that require authentication.)
792 if_none_match = self.headers.getheader('if-none-match')
793 if if_none_match == "abc":
794 self.send_response(304)
795 self.end_headers()
796 else:
797 self.send_response(200)
798 self.send_header('Content-type', 'text/html')
799 self.send_header('Cache-control', 'max-age=60000')
800 self.send_header('Etag', 'abc')
801 self.end_headers()
802 self.wfile.write('<html><head>')
803 self.wfile.write('<title>%s/%s</title>' % (username, password))
804 self.wfile.write('</head><body>')
805 self.wfile.write('auth=%s<p>' % auth)
ericroman@google.com239b4d82009-03-27 04:00:22 +0000806 self.wfile.write('You sent:<br>%s<p>' % self.headers)
initial.commit94958cf2008-07-26 22:42:52 +0000807 self.wfile.write('</body></html>')
808
809 return True
810
811 def AuthDigestHandler(self):
812 """This handler tests 'Digest' authentication. It just sends a page with
813 title 'user/pass' if you succeed."""
814
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000815 if not self._ShouldHandleRequest("/auth-digest"):
initial.commit94958cf2008-07-26 22:42:52 +0000816 return False
817
818 # Periodically generate a new nonce. Technically we should incorporate
819 # the request URL into this, but we don't care for testing.
820 nonce_life = 10
821 stale = False
maruel@google.come250a9b2009-03-10 17:39:46 +0000822 if (not self.server.nonce or
823 (time.time() - self.server.nonce_time > nonce_life)):
initial.commit94958cf2008-07-26 22:42:52 +0000824 if self.server.nonce:
825 stale = True
826 self.server.nonce_time = time.time()
827 self.server.nonce = \
maruel@google.come250a9b2009-03-10 17:39:46 +0000828 _new_md5(time.ctime(self.server.nonce_time) +
829 'privatekey').hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +0000830
831 nonce = self.server.nonce
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +0000832 opaque = _new_md5('opaque').hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +0000833 password = 'secret'
834 realm = 'testrealm'
835
836 auth = self.headers.getheader('authorization')
837 pairs = {}
838 try:
839 if not auth:
840 raise Exception('no auth')
841 if not auth.startswith('Digest'):
842 raise Exception('not digest')
843 # Pull out all the name="value" pairs as a dictionary.
844 pairs = dict(re.findall(r'(\b[^ ,=]+)="?([^",]+)"?', auth))
845
846 # Make sure it's all valid.
847 if pairs['nonce'] != nonce:
848 raise Exception('wrong nonce')
849 if pairs['opaque'] != opaque:
850 raise Exception('wrong opaque')
851
852 # Check the 'response' value and make sure it matches our magic hash.
853 # See http://www.ietf.org/rfc/rfc2617.txt
maruel@google.come250a9b2009-03-10 17:39:46 +0000854 hash_a1 = _new_md5(
855 ':'.join([pairs['username'], realm, password])).hexdigest()
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +0000856 hash_a2 = _new_md5(':'.join([self.command, pairs['uri']])).hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +0000857 if 'qop' in pairs and 'nc' in pairs and 'cnonce' in pairs:
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +0000858 response = _new_md5(':'.join([hash_a1, nonce, pairs['nc'],
initial.commit94958cf2008-07-26 22:42:52 +0000859 pairs['cnonce'], pairs['qop'], hash_a2])).hexdigest()
860 else:
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +0000861 response = _new_md5(':'.join([hash_a1, nonce, hash_a2])).hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +0000862
863 if pairs['response'] != response:
864 raise Exception('wrong password')
865 except Exception, e:
866 # Authentication failed.
867 self.send_response(401)
868 hdr = ('Digest '
869 'realm="%s", '
870 'domain="/", '
871 'qop="auth", '
872 'algorithm=MD5, '
873 'nonce="%s", '
874 'opaque="%s"') % (realm, nonce, opaque)
875 if stale:
876 hdr += ', stale="TRUE"'
877 self.send_header('WWW-Authenticate', hdr)
878 self.send_header('Content-type', 'text/html')
879 self.end_headers()
880 self.wfile.write('<html><head>')
881 self.wfile.write('<title>Denied: %s</title>' % e)
882 self.wfile.write('</head><body>')
883 self.wfile.write('auth=%s<p>' % auth)
884 self.wfile.write('pairs=%s<p>' % pairs)
885 self.wfile.write('You sent:<br>%s<p>' % self.headers)
886 self.wfile.write('We are replying:<br>%s<p>' % hdr)
887 self.wfile.write('</body></html>')
888 return True
889
890 # Authentication successful.
891 self.send_response(200)
892 self.send_header('Content-type', 'text/html')
893 self.end_headers()
894 self.wfile.write('<html><head>')
895 self.wfile.write('<title>%s/%s</title>' % (pairs['username'], password))
896 self.wfile.write('</head><body>')
897 self.wfile.write('auth=%s<p>' % auth)
898 self.wfile.write('pairs=%s<p>' % pairs)
899 self.wfile.write('</body></html>')
900
901 return True
902
903 def SlowServerHandler(self):
904 """Wait for the user suggested time before responding. The syntax is
905 /slow?0.5 to wait for half a second."""
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000906 if not self._ShouldHandleRequest("/slow"):
initial.commit94958cf2008-07-26 22:42:52 +0000907 return False
908 query_char = self.path.find('?')
909 wait_sec = 1.0
910 if query_char >= 0:
911 try:
912 wait_sec = int(self.path[query_char + 1:])
913 except ValueError:
914 pass
915 time.sleep(wait_sec)
916 self.send_response(200)
917 self.send_header('Content-type', 'text/plain')
918 self.end_headers()
919 self.wfile.write("waited %d seconds" % wait_sec)
920 return True
921
922 def ContentTypeHandler(self):
923 """Returns a string of html with the given content type. E.g.,
924 /contenttype?text/css returns an html file with the Content-Type
925 header set to text/css."""
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000926 if not self._ShouldHandleRequest("/contenttype"):
initial.commit94958cf2008-07-26 22:42:52 +0000927 return False
928 query_char = self.path.find('?')
929 content_type = self.path[query_char + 1:].strip()
930 if not content_type:
931 content_type = 'text/html'
932 self.send_response(200)
933 self.send_header('Content-Type', content_type)
934 self.end_headers()
935 self.wfile.write("<html>\n<body>\n<p>HTML text</p>\n</body>\n</html>\n");
936 return True
937
938 def ServerRedirectHandler(self):
939 """Sends a server redirect to the given URL. The syntax is
maruel@google.come250a9b2009-03-10 17:39:46 +0000940 '/server-redirect?http://foo.bar/asdf' to redirect to
941 'http://foo.bar/asdf'"""
initial.commit94958cf2008-07-26 22:42:52 +0000942
943 test_name = "/server-redirect"
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000944 if not self._ShouldHandleRequest(test_name):
initial.commit94958cf2008-07-26 22:42:52 +0000945 return False
946
947 query_char = self.path.find('?')
948 if query_char < 0 or len(self.path) <= query_char + 1:
949 self.sendRedirectHelp(test_name)
950 return True
951 dest = self.path[query_char + 1:]
952
953 self.send_response(301) # moved permanently
954 self.send_header('Location', dest)
955 self.send_header('Content-type', 'text/html')
956 self.end_headers()
957 self.wfile.write('<html><head>')
958 self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
959
wtc@chromium.org743d77b2009-02-11 02:48:15 +0000960 return True
initial.commit94958cf2008-07-26 22:42:52 +0000961
962 def ClientRedirectHandler(self):
963 """Sends a client redirect to the given URL. The syntax is
maruel@google.come250a9b2009-03-10 17:39:46 +0000964 '/client-redirect?http://foo.bar/asdf' to redirect to
965 'http://foo.bar/asdf'"""
initial.commit94958cf2008-07-26 22:42:52 +0000966
967 test_name = "/client-redirect"
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000968 if not self._ShouldHandleRequest(test_name):
initial.commit94958cf2008-07-26 22:42:52 +0000969 return False
970
971 query_char = self.path.find('?');
972 if query_char < 0 or len(self.path) <= query_char + 1:
973 self.sendRedirectHelp(test_name)
974 return True
975 dest = self.path[query_char + 1:]
976
977 self.send_response(200)
978 self.send_header('Content-type', 'text/html')
979 self.end_headers()
980 self.wfile.write('<html><head>')
981 self.wfile.write('<meta http-equiv="refresh" content="0;url=%s">' % dest)
982 self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
983
984 return True
985
986 def DefaultResponseHandler(self):
987 """This is the catch-all response handler for requests that aren't handled
988 by one of the special handlers above.
989 Note that we specify the content-length as without it the https connection
990 is not closed properly (and the browser keeps expecting data)."""
991
992 contents = "Default response given for path: " + self.path
993 self.send_response(200)
994 self.send_header('Content-type', 'text/html')
995 self.send_header("Content-Length", len(contents))
996 self.end_headers()
997 self.wfile.write(contents)
998 return True
999
wtc@chromium.org743d77b2009-02-11 02:48:15 +00001000 def RedirectConnectHandler(self):
1001 """Sends a redirect to the CONNECT request for www.redirect.com. This
1002 response is not specified by the RFC, so the browser should not follow
1003 the redirect."""
1004
1005 if (self.path.find("www.redirect.com") < 0):
1006 return False
1007
1008 dest = "http://www.destination.com/foo.js"
1009
1010 self.send_response(302) # moved temporarily
1011 self.send_header('Location', dest)
1012 self.send_header('Connection', 'close')
1013 self.end_headers()
1014 return True
1015
wtc@chromium.orgb86c7f92009-02-14 01:45:08 +00001016 def ServerAuthConnectHandler(self):
1017 """Sends a 401 to the CONNECT request for www.server-auth.com. This
1018 response doesn't make sense because the proxy server cannot request
1019 server authentication."""
1020
1021 if (self.path.find("www.server-auth.com") < 0):
1022 return False
1023
1024 challenge = 'Basic realm="WallyWorld"'
1025
1026 self.send_response(401) # unauthorized
1027 self.send_header('WWW-Authenticate', challenge)
1028 self.send_header('Connection', 'close')
1029 self.end_headers()
1030 return True
wtc@chromium.org743d77b2009-02-11 02:48:15 +00001031
1032 def DefaultConnectResponseHandler(self):
1033 """This is the catch-all response handler for CONNECT requests that aren't
1034 handled by one of the special handlers above. Real Web servers respond
1035 with 400 to CONNECT requests."""
1036
1037 contents = "Your client has issued a malformed or illegal request."
1038 self.send_response(400) # bad request
1039 self.send_header('Content-type', 'text/html')
1040 self.send_header("Content-Length", len(contents))
1041 self.end_headers()
1042 self.wfile.write(contents)
1043 return True
1044
1045 def do_CONNECT(self):
1046 for handler in self._connect_handlers:
1047 if handler():
1048 return
1049
initial.commit94958cf2008-07-26 22:42:52 +00001050 def do_GET(self):
1051 for handler in self._get_handlers:
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001052 if handler():
initial.commit94958cf2008-07-26 22:42:52 +00001053 return
1054
1055 def do_POST(self):
1056 for handler in self._post_handlers:
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001057 if handler():
initial.commit94958cf2008-07-26 22:42:52 +00001058 return
1059
ananta@chromium.org56d146f2010-01-11 19:03:01 +00001060 def do_PUT(self):
1061 for handler in self._put_handlers:
1062 if handler():
1063 return
1064
initial.commit94958cf2008-07-26 22:42:52 +00001065 # called by the redirect handling function when there is no parameter
1066 def sendRedirectHelp(self, redirect_name):
1067 self.send_response(200)
1068 self.send_header('Content-type', 'text/html')
1069 self.end_headers()
1070 self.wfile.write('<html><body><h1>Error: no redirect destination</h1>')
1071 self.wfile.write('Use <pre>%s?http://dest...</pre>' % redirect_name)
1072 self.wfile.write('</body></html>')
1073
stoyan@chromium.org372692c2009-01-30 17:01:52 +00001074def MakeDumpDir(data_dir):
ananta@chromium.org56d146f2010-01-11 19:03:01 +00001075 """Create directory named 'dump' where uploaded data via HTTP POST/PUT
1076 requests will be stored. If the directory already exists all files and
1077 subdirectories will be deleted."""
stoyan@chromium.org372692c2009-01-30 17:01:52 +00001078 dump_dir = os.path.join(data_dir, 'dump');
1079 if os.path.isdir(dump_dir):
1080 shutil.rmtree(dump_dir)
1081 os.mkdir(dump_dir)
1082
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001083def MakeDataDir():
1084 if options.data_dir:
1085 if not os.path.isdir(options.data_dir):
1086 print 'specified data dir not found: ' + options.data_dir + ' exiting...'
1087 return None
1088 my_data_dir = options.data_dir
1089 else:
1090 # Create the default path to our data dir, relative to the exe dir.
1091 my_data_dir = os.path.dirname(sys.argv[0])
1092 my_data_dir = os.path.join(my_data_dir, "..", "..", "..", "..",
1093 "test", "data")
1094
1095 #TODO(ibrar): Must use Find* funtion defined in google\tools
1096 #i.e my_data_dir = FindUpward(my_data_dir, "test", "data")
1097
1098 return my_data_dir
1099
initial.commit94958cf2008-07-26 22:42:52 +00001100def main(options, args):
1101 # redirect output to a log file so it doesn't spam the unit test output
1102 logfile = open('testserver.log', 'w')
1103 sys.stderr = sys.stdout = logfile
1104
1105 port = options.port
1106
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001107 if options.server_type == SERVER_HTTP:
1108 if options.cert:
1109 # let's make sure the cert file exists.
1110 if not os.path.isfile(options.cert):
1111 print 'specified cert file not found: ' + options.cert + ' exiting...'
1112 return
phajdan.jr@chromium.org599efb82009-08-14 22:37:35 +00001113 if options.forking:
1114 server_class = ForkingHTTPSServer
1115 else:
1116 server_class = HTTPSServer
1117 server = server_class(('127.0.0.1', port), TestPageHandler, options.cert)
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001118 print 'HTTPS server started on port %d...' % port
1119 else:
phajdan.jr@chromium.org599efb82009-08-14 22:37:35 +00001120 if options.forking:
1121 server_class = ForkingHTTPServer
1122 else:
1123 server_class = StoppableHTTPServer
1124 server = server_class(('127.0.0.1', port), TestPageHandler)
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001125 print 'HTTP server started on port %d...' % port
erikkay@google.com70397b62008-12-30 21:49:21 +00001126
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001127 server.data_dir = MakeDataDir()
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +00001128 server.file_root_url = options.file_root_url
stoyan@chromium.org372692c2009-01-30 17:01:52 +00001129 MakeDumpDir(server.data_dir)
maruel@chromium.org756cf982009-03-05 12:46:38 +00001130
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001131 # means FTP Server
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +00001132 else:
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001133 my_data_dir = MakeDataDir()
1134
1135 def line_logger(msg):
1136 if (msg.find("kill") >= 0):
1137 server.stop = True
1138 print 'shutting down server'
1139 sys.exit(0)
1140
1141 # Instantiate a dummy authorizer for managing 'virtual' users
1142 authorizer = pyftpdlib.ftpserver.DummyAuthorizer()
1143
1144 # Define a new user having full r/w permissions and a read-only
1145 # anonymous user
1146 authorizer.add_user('chrome', 'chrome', my_data_dir, perm='elradfmw')
1147
1148 authorizer.add_anonymous(my_data_dir)
1149
1150 # Instantiate FTP handler class
1151 ftp_handler = pyftpdlib.ftpserver.FTPHandler
1152 ftp_handler.authorizer = authorizer
1153 pyftpdlib.ftpserver.logline = line_logger
1154
1155 # Define a customized banner (string returned when client connects)
maruel@google.come250a9b2009-03-10 17:39:46 +00001156 ftp_handler.banner = ("pyftpdlib %s based ftpd ready." %
1157 pyftpdlib.ftpserver.__ver__)
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001158
1159 # Instantiate FTP server class and listen to 127.0.0.1:port
1160 address = ('127.0.0.1', port)
1161 server = pyftpdlib.ftpserver.FTPServer(address, ftp_handler)
1162 print 'FTP server started on port %d...' % port
initial.commit94958cf2008-07-26 22:42:52 +00001163
1164 try:
1165 server.serve_forever()
1166 except KeyboardInterrupt:
1167 print 'shutting down server'
1168 server.stop = True
1169
1170if __name__ == '__main__':
1171 option_parser = optparse.OptionParser()
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001172 option_parser.add_option("-f", '--ftp', action='store_const',
1173 const=SERVER_FTP, default=SERVER_HTTP,
1174 dest='server_type',
1175 help='FTP or HTTP server default HTTP')
phajdan.jr@chromium.org599efb82009-08-14 22:37:35 +00001176 option_parser.add_option('--forking', action='store_true', default=False,
1177 dest='forking',
1178 help='Serve each request in a separate process')
initial.commit94958cf2008-07-26 22:42:52 +00001179 option_parser.add_option('', '--port', default='8888', type='int',
1180 help='Port used by the server')
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001181 option_parser.add_option('', '--data-dir', dest='data_dir',
initial.commit94958cf2008-07-26 22:42:52 +00001182 help='Directory from which to read the files')
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001183 option_parser.add_option('', '--https', dest='cert',
initial.commit94958cf2008-07-26 22:42:52 +00001184 help='Specify that https should be used, specify '
1185 'the path to the cert containing the private key '
1186 'the server should use')
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +00001187 option_parser.add_option('', '--file-root-url', default='/files/',
1188 help='Specify a root URL for files served.')
initial.commit94958cf2008-07-26 22:42:52 +00001189 options, args = option_parser.parse_args()
1190
1191 sys.exit(main(options, args))