blob: a14a3ac7440a1885a0afa240ae2c1dbe59e00c1e [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()
robertshield@chromium.org5e231612010-01-20 18:23:53 +0000185 if options.never_die:
186 self.wfile.write('I cannot die!! BWAHAHA')
187 else:
188 self.wfile.write('Goodbye cruel world!')
189 self.server.stop = True
initial.commit94958cf2008-07-26 22:42:52 +0000190
191 return True
192
193 def NoCacheMaxAgeTimeHandler(self):
194 """This request handler yields a page with the title set to the current
195 system time, and no caching requested."""
196
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000197 if not self._ShouldHandleRequest("/nocachetime/maxage"):
initial.commit94958cf2008-07-26 22:42:52 +0000198 return False
199
200 self.send_response(200)
201 self.send_header('Cache-Control', 'max-age=0')
202 self.send_header('Content-type', 'text/html')
203 self.end_headers()
204
maruel@google.come250a9b2009-03-10 17:39:46 +0000205 self.wfile.write('<html><head><title>%s</title></head></html>' %
206 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000207
208 return True
209
210 def NoCacheTimeHandler(self):
211 """This request handler yields a page with the title set to the current
212 system time, and no caching requested."""
213
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000214 if not self._ShouldHandleRequest("/nocachetime"):
initial.commit94958cf2008-07-26 22:42:52 +0000215 return False
216
217 self.send_response(200)
218 self.send_header('Cache-Control', 'no-cache')
219 self.send_header('Content-type', 'text/html')
220 self.end_headers()
221
maruel@google.come250a9b2009-03-10 17:39:46 +0000222 self.wfile.write('<html><head><title>%s</title></head></html>' %
223 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000224
225 return True
226
227 def CacheTimeHandler(self):
228 """This request handler yields a page with the title set to the current
229 system time, and allows caching for one minute."""
230
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000231 if not self._ShouldHandleRequest("/cachetime"):
initial.commit94958cf2008-07-26 22:42:52 +0000232 return False
233
234 self.send_response(200)
235 self.send_header('Cache-Control', 'max-age=60')
236 self.send_header('Content-type', 'text/html')
237 self.end_headers()
238
maruel@google.come250a9b2009-03-10 17:39:46 +0000239 self.wfile.write('<html><head><title>%s</title></head></html>' %
240 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000241
242 return True
243
244 def CacheExpiresHandler(self):
245 """This request handler yields a page with the title set to the current
246 system time, and set the page to expire on 1 Jan 2099."""
247
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000248 if not self._ShouldHandleRequest("/cache/expires"):
initial.commit94958cf2008-07-26 22:42:52 +0000249 return False
250
251 self.send_response(200)
252 self.send_header('Expires', 'Thu, 1 Jan 2099 00:00:00 GMT')
253 self.send_header('Content-type', 'text/html')
254 self.end_headers()
255
maruel@google.come250a9b2009-03-10 17:39:46 +0000256 self.wfile.write('<html><head><title>%s</title></head></html>' %
257 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000258
259 return True
260
261 def CacheProxyRevalidateHandler(self):
262 """This request handler yields a page with the title set to the current
263 system time, and allows caching for 60 seconds"""
264
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000265 if not self._ShouldHandleRequest("/cache/proxy-revalidate"):
initial.commit94958cf2008-07-26 22:42:52 +0000266 return False
267
268 self.send_response(200)
269 self.send_header('Content-type', 'text/html')
270 self.send_header('Cache-Control', 'max-age=60, proxy-revalidate')
271 self.end_headers()
272
maruel@google.come250a9b2009-03-10 17:39:46 +0000273 self.wfile.write('<html><head><title>%s</title></head></html>' %
274 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000275
276 return True
277
278 def CachePrivateHandler(self):
279 """This request handler yields a page with the title set to the current
280 system time, and allows caching for 5 seconds."""
281
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000282 if not self._ShouldHandleRequest("/cache/private"):
initial.commit94958cf2008-07-26 22:42:52 +0000283 return False
284
285 self.send_response(200)
286 self.send_header('Content-type', 'text/html')
huanr@chromium.orgab5be752009-05-23 02:58:44 +0000287 self.send_header('Cache-Control', 'max-age=3, private')
initial.commit94958cf2008-07-26 22:42:52 +0000288 self.end_headers()
289
maruel@google.come250a9b2009-03-10 17:39:46 +0000290 self.wfile.write('<html><head><title>%s</title></head></html>' %
291 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000292
293 return True
294
295 def CachePublicHandler(self):
296 """This request handler yields a page with the title set to the current
297 system time, and allows caching for 5 seconds."""
298
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000299 if not self._ShouldHandleRequest("/cache/public"):
initial.commit94958cf2008-07-26 22:42:52 +0000300 return False
301
302 self.send_response(200)
303 self.send_header('Content-type', 'text/html')
huanr@chromium.orgab5be752009-05-23 02:58:44 +0000304 self.send_header('Cache-Control', 'max-age=3, public')
initial.commit94958cf2008-07-26 22:42:52 +0000305 self.end_headers()
306
maruel@google.come250a9b2009-03-10 17:39:46 +0000307 self.wfile.write('<html><head><title>%s</title></head></html>' %
308 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000309
310 return True
311
312 def CacheSMaxAgeHandler(self):
313 """This request handler yields a page with the title set to the current
314 system time, and does not allow for caching."""
315
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000316 if not self._ShouldHandleRequest("/cache/s-maxage"):
initial.commit94958cf2008-07-26 22:42:52 +0000317 return False
318
319 self.send_response(200)
320 self.send_header('Content-type', 'text/html')
321 self.send_header('Cache-Control', 'public, s-maxage = 60, max-age = 0')
322 self.end_headers()
323
maruel@google.come250a9b2009-03-10 17:39:46 +0000324 self.wfile.write('<html><head><title>%s</title></head></html>' %
325 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000326
327 return True
328
329 def CacheMustRevalidateHandler(self):
330 """This request handler yields a page with the title set to the current
331 system time, and does not allow caching."""
332
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000333 if not self._ShouldHandleRequest("/cache/must-revalidate"):
initial.commit94958cf2008-07-26 22:42:52 +0000334 return False
335
336 self.send_response(200)
337 self.send_header('Content-type', 'text/html')
338 self.send_header('Cache-Control', 'must-revalidate')
339 self.end_headers()
340
maruel@google.come250a9b2009-03-10 17:39:46 +0000341 self.wfile.write('<html><head><title>%s</title></head></html>' %
342 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000343
344 return True
345
346 def CacheMustRevalidateMaxAgeHandler(self):
347 """This request handler yields a page with the title set to the current
348 system time, and does not allow caching event though max-age of 60
349 seconds is specified."""
350
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000351 if not self._ShouldHandleRequest("/cache/must-revalidate/max-age"):
initial.commit94958cf2008-07-26 22:42:52 +0000352 return False
353
354 self.send_response(200)
355 self.send_header('Content-type', 'text/html')
356 self.send_header('Cache-Control', 'max-age=60, must-revalidate')
357 self.end_headers()
358
maruel@google.come250a9b2009-03-10 17:39:46 +0000359 self.wfile.write('<html><head><title>%s</title></head></html>' %
360 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000361
362 return True
363
initial.commit94958cf2008-07-26 22:42:52 +0000364 def CacheNoStoreHandler(self):
365 """This request handler yields a page with the title set to the current
366 system time, and does not allow the page to be stored."""
367
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000368 if not self._ShouldHandleRequest("/cache/no-store"):
initial.commit94958cf2008-07-26 22:42:52 +0000369 return False
370
371 self.send_response(200)
372 self.send_header('Content-type', 'text/html')
373 self.send_header('Cache-Control', 'no-store')
374 self.end_headers()
375
maruel@google.come250a9b2009-03-10 17:39:46 +0000376 self.wfile.write('<html><head><title>%s</title></head></html>' %
377 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000378
379 return True
380
381 def CacheNoStoreMaxAgeHandler(self):
382 """This request handler yields a page with the title set to the current
383 system time, and does not allow the page to be stored even though max-age
384 of 60 seconds is specified."""
385
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000386 if not self._ShouldHandleRequest("/cache/no-store/max-age"):
initial.commit94958cf2008-07-26 22:42:52 +0000387 return False
388
389 self.send_response(200)
390 self.send_header('Content-type', 'text/html')
391 self.send_header('Cache-Control', 'max-age=60, no-store')
392 self.end_headers()
393
maruel@google.come250a9b2009-03-10 17:39:46 +0000394 self.wfile.write('<html><head><title>%s</title></head></html>' %
395 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000396
397 return True
398
399
400 def CacheNoTransformHandler(self):
401 """This request handler yields a page with the title set to the current
402 system time, and does not allow the content to transformed during
403 user-agent caching"""
404
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000405 if not self._ShouldHandleRequest("/cache/no-transform"):
initial.commit94958cf2008-07-26 22:42:52 +0000406 return False
407
408 self.send_response(200)
409 self.send_header('Content-type', 'text/html')
410 self.send_header('Cache-Control', 'no-transform')
411 self.end_headers()
412
maruel@google.come250a9b2009-03-10 17:39:46 +0000413 self.wfile.write('<html><head><title>%s</title></head></html>' %
414 time.time())
initial.commit94958cf2008-07-26 22:42:52 +0000415
416 return True
417
418 def EchoHeader(self):
419 """This handler echoes back the value of a specific request header."""
ananta@chromium.org219b2062009-10-23 16:09:41 +0000420 """The only difference between this function and the EchoHeaderOverride"""
421 """function is in the parameter being passed to the helper function"""
422 return self.EchoHeaderHelper("/echoheader")
initial.commit94958cf2008-07-26 22:42:52 +0000423
ananta@chromium.org219b2062009-10-23 16:09:41 +0000424 def EchoHeaderOverride(self):
425 """This handler echoes back the value of a specific request header."""
426 """The UrlRequest unit tests also execute for ChromeFrame which uses"""
427 """IE to issue HTTP requests using the host network stack."""
428 """The Accept and Charset tests which expect the server to echo back"""
429 """the corresponding headers fail here as IE returns cached responses"""
430 """The EchoHeaderOverride parameter is an easy way to ensure that IE"""
431 """treats this request as a new request and does not cache it."""
432 return self.EchoHeaderHelper("/echoheaderoverride")
433
434 def EchoHeaderHelper(self, echo_header):
435 """This function echoes back the value of the request header passed in."""
436 if not self._ShouldHandleRequest(echo_header):
initial.commit94958cf2008-07-26 22:42:52 +0000437 return False
438
439 query_char = self.path.find('?')
440 if query_char != -1:
441 header_name = self.path[query_char+1:]
442
443 self.send_response(200)
444 self.send_header('Content-type', 'text/plain')
445 self.send_header('Cache-control', 'max-age=60000')
446 # insert a vary header to properly indicate that the cachability of this
447 # request is subject to value of the request header being echoed.
448 if len(header_name) > 0:
449 self.send_header('Vary', header_name)
450 self.end_headers()
451
452 if len(header_name) > 0:
453 self.wfile.write(self.headers.getheader(header_name))
454
455 return True
456
457 def EchoHandler(self):
458 """This handler just echoes back the payload of the request, for testing
459 form submission."""
460
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000461 if not self._ShouldHandleRequest("/echo"):
initial.commit94958cf2008-07-26 22:42:52 +0000462 return False
463
464 self.send_response(200)
465 self.send_header('Content-type', 'text/html')
466 self.end_headers()
467 length = int(self.headers.getheader('content-length'))
468 request = self.rfile.read(length)
469 self.wfile.write(request)
470 return True
471
stoyan@chromium.org372692c2009-01-30 17:01:52 +0000472 def WriteFile(self):
ananta@chromium.org56d146f2010-01-11 19:03:01 +0000473 """This is handler dumps the content of POST/PUT request to a disk file
474 into the data_dir/dump. Sub-directories are not supported."""
maruel@chromium.org756cf982009-03-05 12:46:38 +0000475
stoyan@chromium.org372692c2009-01-30 17:01:52 +0000476 prefix='/writefile/'
477 if not self.path.startswith(prefix):
478 return False
maruel@chromium.org756cf982009-03-05 12:46:38 +0000479
stoyan@chromium.org372692c2009-01-30 17:01:52 +0000480 file_name = self.path[len(prefix):]
481
482 # do not allow fancy chars in file name
483 re.sub('[^a-zA-Z0-9_.-]+', '', file_name)
484 if len(file_name) and file_name[0] != '.':
485 path = os.path.join(self.server.data_dir, 'dump', file_name);
486 length = int(self.headers.getheader('content-length'))
487 request = self.rfile.read(length)
488 f = open(path, "wb")
489 f.write(request);
490 f.close()
maruel@chromium.org756cf982009-03-05 12:46:38 +0000491
stoyan@chromium.org372692c2009-01-30 17:01:52 +0000492 self.send_response(200)
493 self.send_header('Content-type', 'text/html')
494 self.end_headers()
495 self.wfile.write('<html>%s</html>' % file_name)
496 return True
maruel@chromium.org756cf982009-03-05 12:46:38 +0000497
initial.commit94958cf2008-07-26 22:42:52 +0000498 def EchoTitleHandler(self):
499 """This handler is like Echo, but sets the page title to the request."""
500
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000501 if not self._ShouldHandleRequest("/echotitle"):
initial.commit94958cf2008-07-26 22:42:52 +0000502 return False
503
504 self.send_response(200)
505 self.send_header('Content-type', 'text/html')
506 self.end_headers()
507 length = int(self.headers.getheader('content-length'))
508 request = self.rfile.read(length)
509 self.wfile.write('<html><head><title>')
510 self.wfile.write(request)
511 self.wfile.write('</title></head></html>')
512 return True
513
514 def EchoAllHandler(self):
515 """This handler yields a (more) human-readable page listing information
516 about the request header & contents."""
517
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000518 if not self._ShouldHandleRequest("/echoall"):
initial.commit94958cf2008-07-26 22:42:52 +0000519 return False
520
521 self.send_response(200)
522 self.send_header('Content-type', 'text/html')
523 self.end_headers()
524 self.wfile.write('<html><head><style>'
525 'pre { border: 1px solid black; margin: 5px; padding: 5px }'
526 '</style></head><body>'
527 '<div style="float: right">'
528 '<a href="http://localhost:8888/echo">back to referring page</a></div>'
529 '<h1>Request Body:</h1><pre>')
initial.commit94958cf2008-07-26 22:42:52 +0000530
ananta@chromium.org56d146f2010-01-11 19:03:01 +0000531 if self.command == 'POST' or self.command == 'PUT':
ericroman@google.coma47622b2008-11-15 04:36:51 +0000532 length = int(self.headers.getheader('content-length'))
533 qs = self.rfile.read(length)
534 params = cgi.parse_qs(qs, keep_blank_values=1)
535
536 for param in params:
537 self.wfile.write('%s=%s\n' % (param, params[param][0]))
initial.commit94958cf2008-07-26 22:42:52 +0000538
539 self.wfile.write('</pre>')
540
541 self.wfile.write('<h1>Request Headers:</h1><pre>%s</pre>' % self.headers)
542
543 self.wfile.write('</body></html>')
544 return True
545
546 def DownloadHandler(self):
547 """This handler sends a downloadable file with or without reporting
548 the size (6K)."""
549
550 if self.path.startswith("/download-unknown-size"):
551 send_length = False
552 elif self.path.startswith("/download-known-size"):
553 send_length = True
554 else:
555 return False
556
557 #
558 # The test which uses this functionality is attempting to send
559 # small chunks of data to the client. Use a fairly large buffer
560 # so that we'll fill chrome's IO buffer enough to force it to
561 # actually write the data.
562 # See also the comments in the client-side of this test in
563 # download_uitest.cc
564 #
565 size_chunk1 = 35*1024
566 size_chunk2 = 10*1024
567
568 self.send_response(200)
569 self.send_header('Content-type', 'application/octet-stream')
570 self.send_header('Cache-Control', 'max-age=0')
571 if send_length:
572 self.send_header('Content-Length', size_chunk1 + size_chunk2)
573 self.end_headers()
574
575 # First chunk of data:
576 self.wfile.write("*" * size_chunk1)
577 self.wfile.flush()
578
579 # handle requests until one of them clears this flag.
580 self.server.waitForDownload = True
581 while self.server.waitForDownload:
582 self.server.handle_request()
583
584 # Second chunk of data:
585 self.wfile.write("*" * size_chunk2)
586 return True
587
588 def DownloadFinishHandler(self):
589 """This handler just tells the server to finish the current download."""
590
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000591 if not self._ShouldHandleRequest("/download-finish"):
initial.commit94958cf2008-07-26 22:42:52 +0000592 return False
593
594 self.server.waitForDownload = False
595 self.send_response(200)
596 self.send_header('Content-type', 'text/html')
597 self.send_header('Cache-Control', 'max-age=0')
598 self.end_headers()
599 return True
600
601 def FileHandler(self):
602 """This handler sends the contents of the requested file. Wow, it's like
603 a real webserver!"""
604
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +0000605 prefix = self.server.file_root_url
initial.commit94958cf2008-07-26 22:42:52 +0000606 if not self.path.startswith(prefix):
607 return False
608
darin@chromium.orgc25e5702009-07-23 19:10:23 +0000609 # Consume a request body if present.
ananta@chromium.org56d146f2010-01-11 19:03:01 +0000610 if self.command == 'POST' or self.command == 'PUT' :
darin@chromium.orgc25e5702009-07-23 19:10:23 +0000611 self.rfile.read(int(self.headers.getheader('content-length')))
612
initial.commit94958cf2008-07-26 22:42:52 +0000613 file = self.path[len(prefix):]
finnur@chromium.org88e84c32009-10-02 17:59:55 +0000614 if file.find('?') > -1:
615 # Ignore the query parameters entirely.
616 url, querystring = file.split('?')
617 else:
618 url = file
619 entries = url.split('/')
initial.commit94958cf2008-07-26 22:42:52 +0000620 path = os.path.join(self.server.data_dir, *entries)
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +0000621 if os.path.isdir(path):
622 path = os.path.join(path, 'index.html')
initial.commit94958cf2008-07-26 22:42:52 +0000623
624 if not os.path.isfile(path):
625 print "File not found " + file + " full path:" + path
626 self.send_error(404)
627 return True
628
629 f = open(path, "rb")
630 data = f.read()
631 f.close()
632
633 # If file.mock-http-headers exists, it contains the headers we
634 # should send. Read them in and parse them.
635 headers_path = path + '.mock-http-headers'
636 if os.path.isfile(headers_path):
637 f = open(headers_path, "r")
638
639 # "HTTP/1.1 200 OK"
640 response = f.readline()
641 status_code = re.findall('HTTP/\d+.\d+ (\d+)', response)[0]
642 self.send_response(int(status_code))
643
644 for line in f:
robertshield@chromium.org5e231612010-01-20 18:23:53 +0000645 header_values = re.findall('(\S+):\s*(.*)', line)
646 if len(header_values) > 0:
647 # "name: value"
648 name, value = header_values[0]
649 self.send_header(name, value)
initial.commit94958cf2008-07-26 22:42:52 +0000650 f.close()
651 else:
652 # Could be more generic once we support mime-type sniffing, but for
653 # now we need to set it explicitly.
654 self.send_response(200)
655 self.send_header('Content-type', self.GetMIMETypeFromName(file))
656 self.send_header('Content-Length', len(data))
657 self.end_headers()
658
659 self.wfile.write(data)
660
661 return True
662
663 def RealFileWithCommonHeaderHandler(self):
664 """This handler sends the contents of the requested file without the pseudo
665 http head!"""
666
667 prefix='/realfiles/'
668 if not self.path.startswith(prefix):
669 return False
670
671 file = self.path[len(prefix):]
672 path = os.path.join(self.server.data_dir, file)
673
674 try:
675 f = open(path, "rb")
676 data = f.read()
677 f.close()
678
679 # just simply set the MIME as octal stream
680 self.send_response(200)
681 self.send_header('Content-type', 'application/octet-stream')
682 self.end_headers()
683
684 self.wfile.write(data)
685 except:
686 self.send_error(404)
687
688 return True
689
690 def RealBZ2FileWithCommonHeaderHandler(self):
691 """This handler sends the bzip2 contents of the requested file with
692 corresponding Content-Encoding field in http head!"""
693
694 prefix='/realbz2files/'
695 if not self.path.startswith(prefix):
696 return False
697
698 parts = self.path.split('?')
699 file = parts[0][len(prefix):]
700 path = os.path.join(self.server.data_dir, file) + '.bz2'
701
702 if len(parts) > 1:
703 options = parts[1]
704 else:
705 options = ''
706
707 try:
708 self.send_response(200)
709 accept_encoding = self.headers.get("Accept-Encoding")
710 if accept_encoding.find("bzip2") != -1:
711 f = open(path, "rb")
712 data = f.read()
713 f.close()
714 self.send_header('Content-Encoding', 'bzip2')
715 self.send_header('Content-type', 'application/x-bzip2')
716 self.end_headers()
717 if options == 'incremental-header':
718 self.wfile.write(data[:1])
719 self.wfile.flush()
720 time.sleep(1.0)
721 self.wfile.write(data[1:])
722 else:
723 self.wfile.write(data)
724 else:
725 """client do not support bzip2 format, send pseudo content
726 """
727 self.send_header('Content-type', 'text/html; charset=ISO-8859-1')
728 self.end_headers()
729 self.wfile.write("you do not support bzip2 encoding")
730 except:
731 self.send_error(404)
732
733 return True
734
levin@chromium.orgf7ee2e42009-08-26 02:33:46 +0000735 def SetCookieHandler(self):
736 """This handler just sets a cookie, for testing cookie handling."""
737
738 if not self._ShouldHandleRequest("/set-cookie"):
739 return False
740
741 query_char = self.path.find('?')
742 if query_char != -1:
743 cookie_values = self.path[query_char + 1:].split('&')
744 else:
745 cookie_values = ("",)
746 self.send_response(200)
747 self.send_header('Content-type', 'text/html')
748 for cookie_value in cookie_values:
749 self.send_header('Set-Cookie', '%s' % cookie_value)
750 self.end_headers()
751 for cookie_value in cookie_values:
752 self.wfile.write('%s' % cookie_value)
753 return True
754
initial.commit94958cf2008-07-26 22:42:52 +0000755 def AuthBasicHandler(self):
756 """This handler tests 'Basic' authentication. It just sends a page with
757 title 'user/pass' if you succeed."""
758
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000759 if not self._ShouldHandleRequest("/auth-basic"):
initial.commit94958cf2008-07-26 22:42:52 +0000760 return False
761
762 username = userpass = password = b64str = ""
763
ericroman@google.com239b4d82009-03-27 04:00:22 +0000764 set_cookie_if_challenged = self.path.find('?set-cookie-if-challenged') > 0
765
initial.commit94958cf2008-07-26 22:42:52 +0000766 auth = self.headers.getheader('authorization')
767 try:
768 if not auth:
769 raise Exception('no auth')
770 b64str = re.findall(r'Basic (\S+)', auth)[0]
771 userpass = base64.b64decode(b64str)
772 username, password = re.findall(r'([^:]+):(\S+)', userpass)[0]
773 if password != 'secret':
774 raise Exception('wrong password')
775 except Exception, e:
776 # Authentication failed.
777 self.send_response(401)
778 self.send_header('WWW-Authenticate', 'Basic realm="testrealm"')
779 self.send_header('Content-type', 'text/html')
ericroman@google.com239b4d82009-03-27 04:00:22 +0000780 if set_cookie_if_challenged:
781 self.send_header('Set-Cookie', 'got_challenged=true')
initial.commit94958cf2008-07-26 22:42:52 +0000782 self.end_headers()
783 self.wfile.write('<html><head>')
784 self.wfile.write('<title>Denied: %s</title>' % e)
785 self.wfile.write('</head><body>')
786 self.wfile.write('auth=%s<p>' % auth)
787 self.wfile.write('b64str=%s<p>' % b64str)
788 self.wfile.write('username: %s<p>' % username)
789 self.wfile.write('userpass: %s<p>' % userpass)
790 self.wfile.write('password: %s<p>' % password)
791 self.wfile.write('You sent:<br>%s<p>' % self.headers)
792 self.wfile.write('</body></html>')
793 return True
794
795 # Authentication successful. (Return a cachable response to allow for
796 # testing cached pages that require authentication.)
797 if_none_match = self.headers.getheader('if-none-match')
798 if if_none_match == "abc":
799 self.send_response(304)
800 self.end_headers()
801 else:
802 self.send_response(200)
803 self.send_header('Content-type', 'text/html')
804 self.send_header('Cache-control', 'max-age=60000')
805 self.send_header('Etag', 'abc')
806 self.end_headers()
807 self.wfile.write('<html><head>')
808 self.wfile.write('<title>%s/%s</title>' % (username, password))
809 self.wfile.write('</head><body>')
810 self.wfile.write('auth=%s<p>' % auth)
ericroman@google.com239b4d82009-03-27 04:00:22 +0000811 self.wfile.write('You sent:<br>%s<p>' % self.headers)
initial.commit94958cf2008-07-26 22:42:52 +0000812 self.wfile.write('</body></html>')
813
814 return True
815
816 def AuthDigestHandler(self):
817 """This handler tests 'Digest' authentication. It just sends a page with
818 title 'user/pass' if you succeed."""
819
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000820 if not self._ShouldHandleRequest("/auth-digest"):
initial.commit94958cf2008-07-26 22:42:52 +0000821 return False
822
823 # Periodically generate a new nonce. Technically we should incorporate
824 # the request URL into this, but we don't care for testing.
825 nonce_life = 10
826 stale = False
maruel@google.come250a9b2009-03-10 17:39:46 +0000827 if (not self.server.nonce or
828 (time.time() - self.server.nonce_time > nonce_life)):
initial.commit94958cf2008-07-26 22:42:52 +0000829 if self.server.nonce:
830 stale = True
831 self.server.nonce_time = time.time()
832 self.server.nonce = \
maruel@google.come250a9b2009-03-10 17:39:46 +0000833 _new_md5(time.ctime(self.server.nonce_time) +
834 'privatekey').hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +0000835
836 nonce = self.server.nonce
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +0000837 opaque = _new_md5('opaque').hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +0000838 password = 'secret'
839 realm = 'testrealm'
840
841 auth = self.headers.getheader('authorization')
842 pairs = {}
843 try:
844 if not auth:
845 raise Exception('no auth')
846 if not auth.startswith('Digest'):
847 raise Exception('not digest')
848 # Pull out all the name="value" pairs as a dictionary.
849 pairs = dict(re.findall(r'(\b[^ ,=]+)="?([^",]+)"?', auth))
850
851 # Make sure it's all valid.
852 if pairs['nonce'] != nonce:
853 raise Exception('wrong nonce')
854 if pairs['opaque'] != opaque:
855 raise Exception('wrong opaque')
856
857 # Check the 'response' value and make sure it matches our magic hash.
858 # See http://www.ietf.org/rfc/rfc2617.txt
maruel@google.come250a9b2009-03-10 17:39:46 +0000859 hash_a1 = _new_md5(
860 ':'.join([pairs['username'], realm, password])).hexdigest()
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +0000861 hash_a2 = _new_md5(':'.join([self.command, pairs['uri']])).hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +0000862 if 'qop' in pairs and 'nc' in pairs and 'cnonce' in pairs:
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +0000863 response = _new_md5(':'.join([hash_a1, nonce, pairs['nc'],
initial.commit94958cf2008-07-26 22:42:52 +0000864 pairs['cnonce'], pairs['qop'], hash_a2])).hexdigest()
865 else:
thomasvl@chromium.org595be8d2009-03-06 19:59:26 +0000866 response = _new_md5(':'.join([hash_a1, nonce, hash_a2])).hexdigest()
initial.commit94958cf2008-07-26 22:42:52 +0000867
868 if pairs['response'] != response:
869 raise Exception('wrong password')
870 except Exception, e:
871 # Authentication failed.
872 self.send_response(401)
873 hdr = ('Digest '
874 'realm="%s", '
875 'domain="/", '
876 'qop="auth", '
877 'algorithm=MD5, '
878 'nonce="%s", '
879 'opaque="%s"') % (realm, nonce, opaque)
880 if stale:
881 hdr += ', stale="TRUE"'
882 self.send_header('WWW-Authenticate', hdr)
883 self.send_header('Content-type', 'text/html')
884 self.end_headers()
885 self.wfile.write('<html><head>')
886 self.wfile.write('<title>Denied: %s</title>' % e)
887 self.wfile.write('</head><body>')
888 self.wfile.write('auth=%s<p>' % auth)
889 self.wfile.write('pairs=%s<p>' % pairs)
890 self.wfile.write('You sent:<br>%s<p>' % self.headers)
891 self.wfile.write('We are replying:<br>%s<p>' % hdr)
892 self.wfile.write('</body></html>')
893 return True
894
895 # Authentication successful.
896 self.send_response(200)
897 self.send_header('Content-type', 'text/html')
898 self.end_headers()
899 self.wfile.write('<html><head>')
900 self.wfile.write('<title>%s/%s</title>' % (pairs['username'], password))
901 self.wfile.write('</head><body>')
902 self.wfile.write('auth=%s<p>' % auth)
903 self.wfile.write('pairs=%s<p>' % pairs)
904 self.wfile.write('</body></html>')
905
906 return True
907
908 def SlowServerHandler(self):
909 """Wait for the user suggested time before responding. The syntax is
910 /slow?0.5 to wait for half a second."""
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000911 if not self._ShouldHandleRequest("/slow"):
initial.commit94958cf2008-07-26 22:42:52 +0000912 return False
913 query_char = self.path.find('?')
914 wait_sec = 1.0
915 if query_char >= 0:
916 try:
917 wait_sec = int(self.path[query_char + 1:])
918 except ValueError:
919 pass
920 time.sleep(wait_sec)
921 self.send_response(200)
922 self.send_header('Content-type', 'text/plain')
923 self.end_headers()
924 self.wfile.write("waited %d seconds" % wait_sec)
925 return True
926
927 def ContentTypeHandler(self):
928 """Returns a string of html with the given content type. E.g.,
929 /contenttype?text/css returns an html file with the Content-Type
930 header set to text/css."""
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000931 if not self._ShouldHandleRequest("/contenttype"):
initial.commit94958cf2008-07-26 22:42:52 +0000932 return False
933 query_char = self.path.find('?')
934 content_type = self.path[query_char + 1:].strip()
935 if not content_type:
936 content_type = 'text/html'
937 self.send_response(200)
938 self.send_header('Content-Type', content_type)
939 self.end_headers()
940 self.wfile.write("<html>\n<body>\n<p>HTML text</p>\n</body>\n</html>\n");
941 return True
942
943 def ServerRedirectHandler(self):
944 """Sends a server redirect to the given URL. The syntax is
maruel@google.come250a9b2009-03-10 17:39:46 +0000945 '/server-redirect?http://foo.bar/asdf' to redirect to
946 'http://foo.bar/asdf'"""
initial.commit94958cf2008-07-26 22:42:52 +0000947
948 test_name = "/server-redirect"
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000949 if not self._ShouldHandleRequest(test_name):
initial.commit94958cf2008-07-26 22:42:52 +0000950 return False
951
952 query_char = self.path.find('?')
953 if query_char < 0 or len(self.path) <= query_char + 1:
954 self.sendRedirectHelp(test_name)
955 return True
956 dest = self.path[query_char + 1:]
957
958 self.send_response(301) # moved permanently
959 self.send_header('Location', dest)
960 self.send_header('Content-type', 'text/html')
961 self.end_headers()
962 self.wfile.write('<html><head>')
963 self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
964
wtc@chromium.org743d77b2009-02-11 02:48:15 +0000965 return True
initial.commit94958cf2008-07-26 22:42:52 +0000966
967 def ClientRedirectHandler(self):
968 """Sends a client redirect to the given URL. The syntax is
maruel@google.come250a9b2009-03-10 17:39:46 +0000969 '/client-redirect?http://foo.bar/asdf' to redirect to
970 'http://foo.bar/asdf'"""
initial.commit94958cf2008-07-26 22:42:52 +0000971
972 test_name = "/client-redirect"
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +0000973 if not self._ShouldHandleRequest(test_name):
initial.commit94958cf2008-07-26 22:42:52 +0000974 return False
975
976 query_char = self.path.find('?');
977 if query_char < 0 or len(self.path) <= query_char + 1:
978 self.sendRedirectHelp(test_name)
979 return True
980 dest = self.path[query_char + 1:]
981
982 self.send_response(200)
983 self.send_header('Content-type', 'text/html')
984 self.end_headers()
985 self.wfile.write('<html><head>')
986 self.wfile.write('<meta http-equiv="refresh" content="0;url=%s">' % dest)
987 self.wfile.write('</head><body>Redirecting to %s</body></html>' % dest)
988
989 return True
990
991 def DefaultResponseHandler(self):
992 """This is the catch-all response handler for requests that aren't handled
993 by one of the special handlers above.
994 Note that we specify the content-length as without it the https connection
995 is not closed properly (and the browser keeps expecting data)."""
996
997 contents = "Default response given for path: " + self.path
998 self.send_response(200)
999 self.send_header('Content-type', 'text/html')
1000 self.send_header("Content-Length", len(contents))
1001 self.end_headers()
1002 self.wfile.write(contents)
1003 return True
1004
wtc@chromium.org743d77b2009-02-11 02:48:15 +00001005 def RedirectConnectHandler(self):
1006 """Sends a redirect to the CONNECT request for www.redirect.com. This
1007 response is not specified by the RFC, so the browser should not follow
1008 the redirect."""
1009
1010 if (self.path.find("www.redirect.com") < 0):
1011 return False
1012
1013 dest = "http://www.destination.com/foo.js"
1014
1015 self.send_response(302) # moved temporarily
1016 self.send_header('Location', dest)
1017 self.send_header('Connection', 'close')
1018 self.end_headers()
1019 return True
1020
wtc@chromium.orgb86c7f92009-02-14 01:45:08 +00001021 def ServerAuthConnectHandler(self):
1022 """Sends a 401 to the CONNECT request for www.server-auth.com. This
1023 response doesn't make sense because the proxy server cannot request
1024 server authentication."""
1025
1026 if (self.path.find("www.server-auth.com") < 0):
1027 return False
1028
1029 challenge = 'Basic realm="WallyWorld"'
1030
1031 self.send_response(401) # unauthorized
1032 self.send_header('WWW-Authenticate', challenge)
1033 self.send_header('Connection', 'close')
1034 self.end_headers()
1035 return True
wtc@chromium.org743d77b2009-02-11 02:48:15 +00001036
1037 def DefaultConnectResponseHandler(self):
1038 """This is the catch-all response handler for CONNECT requests that aren't
1039 handled by one of the special handlers above. Real Web servers respond
1040 with 400 to CONNECT requests."""
1041
1042 contents = "Your client has issued a malformed or illegal request."
1043 self.send_response(400) # bad request
1044 self.send_header('Content-type', 'text/html')
1045 self.send_header("Content-Length", len(contents))
1046 self.end_headers()
1047 self.wfile.write(contents)
1048 return True
1049
1050 def do_CONNECT(self):
1051 for handler in self._connect_handlers:
1052 if handler():
1053 return
1054
initial.commit94958cf2008-07-26 22:42:52 +00001055 def do_GET(self):
1056 for handler in self._get_handlers:
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001057 if handler():
initial.commit94958cf2008-07-26 22:42:52 +00001058 return
1059
1060 def do_POST(self):
1061 for handler in self._post_handlers:
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001062 if handler():
initial.commit94958cf2008-07-26 22:42:52 +00001063 return
1064
ananta@chromium.org56d146f2010-01-11 19:03:01 +00001065 def do_PUT(self):
1066 for handler in self._put_handlers:
1067 if handler():
1068 return
1069
initial.commit94958cf2008-07-26 22:42:52 +00001070 # called by the redirect handling function when there is no parameter
1071 def sendRedirectHelp(self, redirect_name):
1072 self.send_response(200)
1073 self.send_header('Content-type', 'text/html')
1074 self.end_headers()
1075 self.wfile.write('<html><body><h1>Error: no redirect destination</h1>')
1076 self.wfile.write('Use <pre>%s?http://dest...</pre>' % redirect_name)
1077 self.wfile.write('</body></html>')
1078
stoyan@chromium.org372692c2009-01-30 17:01:52 +00001079def MakeDumpDir(data_dir):
ananta@chromium.org56d146f2010-01-11 19:03:01 +00001080 """Create directory named 'dump' where uploaded data via HTTP POST/PUT
1081 requests will be stored. If the directory already exists all files and
1082 subdirectories will be deleted."""
stoyan@chromium.org372692c2009-01-30 17:01:52 +00001083 dump_dir = os.path.join(data_dir, 'dump');
1084 if os.path.isdir(dump_dir):
1085 shutil.rmtree(dump_dir)
1086 os.mkdir(dump_dir)
1087
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001088def MakeDataDir():
1089 if options.data_dir:
1090 if not os.path.isdir(options.data_dir):
1091 print 'specified data dir not found: ' + options.data_dir + ' exiting...'
1092 return None
1093 my_data_dir = options.data_dir
1094 else:
1095 # Create the default path to our data dir, relative to the exe dir.
1096 my_data_dir = os.path.dirname(sys.argv[0])
1097 my_data_dir = os.path.join(my_data_dir, "..", "..", "..", "..",
1098 "test", "data")
1099
1100 #TODO(ibrar): Must use Find* funtion defined in google\tools
1101 #i.e my_data_dir = FindUpward(my_data_dir, "test", "data")
1102
1103 return my_data_dir
1104
initial.commit94958cf2008-07-26 22:42:52 +00001105def main(options, args):
1106 # redirect output to a log file so it doesn't spam the unit test output
1107 logfile = open('testserver.log', 'w')
1108 sys.stderr = sys.stdout = logfile
1109
1110 port = options.port
1111
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001112 if options.server_type == SERVER_HTTP:
1113 if options.cert:
1114 # let's make sure the cert file exists.
1115 if not os.path.isfile(options.cert):
1116 print 'specified cert file not found: ' + options.cert + ' exiting...'
1117 return
phajdan.jr@chromium.org599efb82009-08-14 22:37:35 +00001118 if options.forking:
1119 server_class = ForkingHTTPSServer
1120 else:
1121 server_class = HTTPSServer
1122 server = server_class(('127.0.0.1', port), TestPageHandler, options.cert)
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001123 print 'HTTPS server started on port %d...' % port
1124 else:
phajdan.jr@chromium.org599efb82009-08-14 22:37:35 +00001125 if options.forking:
1126 server_class = ForkingHTTPServer
1127 else:
1128 server_class = StoppableHTTPServer
1129 server = server_class(('127.0.0.1', port), TestPageHandler)
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001130 print 'HTTP server started on port %d...' % port
erikkay@google.com70397b62008-12-30 21:49:21 +00001131
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001132 server.data_dir = MakeDataDir()
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +00001133 server.file_root_url = options.file_root_url
stoyan@chromium.org372692c2009-01-30 17:01:52 +00001134 MakeDumpDir(server.data_dir)
maruel@chromium.org756cf982009-03-05 12:46:38 +00001135
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001136 # means FTP Server
nsylvain@chromium.org8d5763b2008-12-30 23:44:27 +00001137 else:
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001138 my_data_dir = MakeDataDir()
1139
1140 def line_logger(msg):
1141 if (msg.find("kill") >= 0):
1142 server.stop = True
1143 print 'shutting down server'
1144 sys.exit(0)
1145
1146 # Instantiate a dummy authorizer for managing 'virtual' users
1147 authorizer = pyftpdlib.ftpserver.DummyAuthorizer()
1148
1149 # Define a new user having full r/w permissions and a read-only
1150 # anonymous user
1151 authorizer.add_user('chrome', 'chrome', my_data_dir, perm='elradfmw')
1152
1153 authorizer.add_anonymous(my_data_dir)
1154
1155 # Instantiate FTP handler class
1156 ftp_handler = pyftpdlib.ftpserver.FTPHandler
1157 ftp_handler.authorizer = authorizer
1158 pyftpdlib.ftpserver.logline = line_logger
1159
1160 # Define a customized banner (string returned when client connects)
maruel@google.come250a9b2009-03-10 17:39:46 +00001161 ftp_handler.banner = ("pyftpdlib %s based ftpd ready." %
1162 pyftpdlib.ftpserver.__ver__)
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001163
1164 # Instantiate FTP server class and listen to 127.0.0.1:port
1165 address = ('127.0.0.1', port)
1166 server = pyftpdlib.ftpserver.FTPServer(address, ftp_handler)
1167 print 'FTP server started on port %d...' % port
initial.commit94958cf2008-07-26 22:42:52 +00001168
1169 try:
1170 server.serve_forever()
1171 except KeyboardInterrupt:
1172 print 'shutting down server'
1173 server.stop = True
1174
1175if __name__ == '__main__':
1176 option_parser = optparse.OptionParser()
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001177 option_parser.add_option("-f", '--ftp', action='store_const',
1178 const=SERVER_FTP, default=SERVER_HTTP,
1179 dest='server_type',
robertshield@chromium.org5e231612010-01-20 18:23:53 +00001180 help='FTP or HTTP server: default is HTTP.')
phajdan.jr@chromium.org599efb82009-08-14 22:37:35 +00001181 option_parser.add_option('--forking', action='store_true', default=False,
1182 dest='forking',
robertshield@chromium.org5e231612010-01-20 18:23:53 +00001183 help='Serve each request in a separate process.')
initial.commit94958cf2008-07-26 22:42:52 +00001184 option_parser.add_option('', '--port', default='8888', type='int',
robertshield@chromium.org5e231612010-01-20 18:23:53 +00001185 help='Port used by the server.')
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001186 option_parser.add_option('', '--data-dir', dest='data_dir',
robertshield@chromium.org5e231612010-01-20 18:23:53 +00001187 help='Directory from which to read the files.')
erikkay@google.comd5182ff2009-01-08 20:45:27 +00001188 option_parser.add_option('', '--https', dest='cert',
initial.commit94958cf2008-07-26 22:42:52 +00001189 help='Specify that https should be used, specify '
1190 'the path to the cert containing the private key '
robertshield@chromium.org5e231612010-01-20 18:23:53 +00001191 'the server should use.')
ben@chromium.org0c7ac3a2009-04-10 02:37:22 +00001192 option_parser.add_option('', '--file-root-url', default='/files/',
1193 help='Specify a root URL for files served.')
robertshield@chromium.org5e231612010-01-20 18:23:53 +00001194 option_parser.add_option('', '--never-die', default=False,
1195 action="store_true",
1196 help='Prevent the server from dying when visiting '
1197 'a /kill URL. Useful for manually running some '
1198 'tests.')
initial.commit94958cf2008-07-26 22:42:52 +00001199 options, args = option_parser.parse_args()
1200
1201 sys.exit(main(options, args))