• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Unittests for the various HTTPServer modules.
2
3Written by Cody A.W. Somerville <cody-somerville@ubuntu.com>,
4Josip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest.
5"""
6
7import os
8import sys
9import re
10import base64
11import ntpath
12import shutil
13import urllib
14import httplib
15import tempfile
16import unittest
17import CGIHTTPServer
18
19
20from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
21from SimpleHTTPServer import SimpleHTTPRequestHandler
22from CGIHTTPServer import CGIHTTPRequestHandler
23from StringIO import StringIO
24from test import test_support
25
26
27threading = test_support.import_module('threading')
28
29
30class NoLogRequestHandler:
31    def log_message(self, *args):
32        # don't write log messages to stderr
33        pass
34
35class SocketlessRequestHandler(SimpleHTTPRequestHandler):
36    def __init__(self):
37        self.get_called = False
38        self.protocol_version = "HTTP/1.1"
39
40    def do_GET(self):
41        self.get_called = True
42        self.send_response(200)
43        self.send_header('Content-Type', 'text/html')
44        self.end_headers()
45        self.wfile.write(b'<html><body>Data</body></html>\r\n')
46
47    def log_message(self, fmt, *args):
48        pass
49
50
51class TestServerThread(threading.Thread):
52    def __init__(self, test_object, request_handler):
53        threading.Thread.__init__(self)
54        self.request_handler = request_handler
55        self.test_object = test_object
56
57    def run(self):
58        self.server = HTTPServer(('', 0), self.request_handler)
59        self.test_object.PORT = self.server.socket.getsockname()[1]
60        self.test_object.server_started.set()
61        self.test_object = None
62        try:
63            self.server.serve_forever(0.05)
64        finally:
65            self.server.server_close()
66
67    def stop(self):
68        self.server.shutdown()
69
70
71class BaseTestCase(unittest.TestCase):
72    def setUp(self):
73        self._threads = test_support.threading_setup()
74        os.environ = test_support.EnvironmentVarGuard()
75        self.server_started = threading.Event()
76        self.thread = TestServerThread(self, self.request_handler)
77        self.thread.start()
78        self.server_started.wait()
79
80    def tearDown(self):
81        self.thread.stop()
82        os.environ.__exit__()
83        test_support.threading_cleanup(*self._threads)
84
85    def request(self, uri, method='GET', body=None, headers={}):
86        self.connection = httplib.HTTPConnection('localhost', self.PORT)
87        self.connection.request(method, uri, body, headers)
88        return self.connection.getresponse()
89
90class BaseHTTPRequestHandlerTestCase(unittest.TestCase):
91    """Test the functionality of the BaseHTTPServer focussing on
92    BaseHTTPRequestHandler.
93    """
94
95    HTTPResponseMatch = re.compile('HTTP/1.[0-9]+ 200 OK')
96
97    def setUp (self):
98        self.handler = SocketlessRequestHandler()
99
100    def send_typical_request(self, message):
101        input_msg = StringIO(message)
102        output = StringIO()
103        self.handler.rfile = input_msg
104        self.handler.wfile = output
105        self.handler.handle_one_request()
106        output.seek(0)
107        return output.readlines()
108
109    def verify_get_called(self):
110        self.assertTrue(self.handler.get_called)
111
112    def verify_expected_headers(self, headers):
113        for fieldName in 'Server: ', 'Date: ', 'Content-Type: ':
114            self.assertEqual(sum(h.startswith(fieldName) for h in headers), 1)
115
116    def verify_http_server_response(self, response):
117        match = self.HTTPResponseMatch.search(response)
118        self.assertIsNotNone(match)
119
120    def test_http_1_1(self):
121        result = self.send_typical_request('GET / HTTP/1.1\r\n\r\n')
122        self.verify_http_server_response(result[0])
123        self.verify_expected_headers(result[1:-1])
124        self.verify_get_called()
125        self.assertEqual(result[-1], '<html><body>Data</body></html>\r\n')
126
127    def test_http_1_0(self):
128        result = self.send_typical_request('GET / HTTP/1.0\r\n\r\n')
129        self.verify_http_server_response(result[0])
130        self.verify_expected_headers(result[1:-1])
131        self.verify_get_called()
132        self.assertEqual(result[-1], '<html><body>Data</body></html>\r\n')
133
134    def test_http_0_9(self):
135        result = self.send_typical_request('GET / HTTP/0.9\r\n\r\n')
136        self.assertEqual(len(result), 1)
137        self.assertEqual(result[0], '<html><body>Data</body></html>\r\n')
138        self.verify_get_called()
139
140    def test_with_continue_1_0(self):
141        result = self.send_typical_request('GET / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n')
142        self.verify_http_server_response(result[0])
143        self.verify_expected_headers(result[1:-1])
144        self.verify_get_called()
145        self.assertEqual(result[-1], '<html><body>Data</body></html>\r\n')
146
147    def test_request_length(self):
148        # Issue #10714: huge request lines are discarded, to avoid Denial
149        # of Service attacks.
150        result = self.send_typical_request(b'GET ' + b'x' * 65537)
151        self.assertEqual(result[0], b'HTTP/1.1 414 Request-URI Too Long\r\n')
152        self.assertFalse(self.handler.get_called)
153
154
155class BaseHTTPServerTestCase(BaseTestCase):
156    class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler):
157        protocol_version = 'HTTP/1.1'
158        default_request_version = 'HTTP/1.1'
159
160        def do_TEST(self):
161            self.send_response(204)
162            self.send_header('Content-Type', 'text/html')
163            self.send_header('Connection', 'close')
164            self.end_headers()
165
166        def do_KEEP(self):
167            self.send_response(204)
168            self.send_header('Content-Type', 'text/html')
169            self.send_header('Connection', 'keep-alive')
170            self.end_headers()
171
172        def do_KEYERROR(self):
173            self.send_error(999)
174
175        def do_CUSTOM(self):
176            self.send_response(999)
177            self.send_header('Content-Type', 'text/html')
178            self.send_header('Connection', 'close')
179            self.end_headers()
180
181        def do_SEND_ERROR(self):
182            self.send_error(int(self.path[1:]))
183
184        def do_HEAD(self):
185            self.send_error(int(self.path[1:]))
186
187    def setUp(self):
188        BaseTestCase.setUp(self)
189        self.con = httplib.HTTPConnection('localhost', self.PORT)
190        self.con.connect()
191
192    def test_command(self):
193        self.con.request('GET', '/')
194        res = self.con.getresponse()
195        self.assertEqual(res.status, 501)
196
197    def test_request_line_trimming(self):
198        self.con._http_vsn_str = 'HTTP/1.1\n'
199        self.con.putrequest('XYZBOGUS', '/')
200        self.con.endheaders()
201        res = self.con.getresponse()
202        self.assertEqual(res.status, 501)
203
204    def test_version_bogus(self):
205        self.con._http_vsn_str = 'FUBAR'
206        self.con.putrequest('GET', '/')
207        self.con.endheaders()
208        res = self.con.getresponse()
209        self.assertEqual(res.status, 400)
210
211    def test_version_digits(self):
212        self.con._http_vsn_str = 'HTTP/9.9.9'
213        self.con.putrequest('GET', '/')
214        self.con.endheaders()
215        res = self.con.getresponse()
216        self.assertEqual(res.status, 400)
217
218    def test_version_none_get(self):
219        self.con._http_vsn_str = ''
220        self.con.putrequest('GET', '/')
221        self.con.endheaders()
222        res = self.con.getresponse()
223        self.assertEqual(res.status, 501)
224
225    def test_version_none(self):
226        # Test that a valid method is rejected when not HTTP/1.x
227        self.con._http_vsn_str = ''
228        self.con.putrequest('CUSTOM', '/')
229        self.con.endheaders()
230        res = self.con.getresponse()
231        self.assertEqual(res.status, 400)
232
233    def test_version_invalid(self):
234        self.con._http_vsn = 99
235        self.con._http_vsn_str = 'HTTP/9.9'
236        self.con.putrequest('GET', '/')
237        self.con.endheaders()
238        res = self.con.getresponse()
239        self.assertEqual(res.status, 505)
240
241    def test_send_blank(self):
242        self.con._http_vsn_str = ''
243        self.con.putrequest('', '')
244        self.con.endheaders()
245        res = self.con.getresponse()
246        self.assertEqual(res.status, 400)
247
248    def test_header_close(self):
249        self.con.putrequest('GET', '/')
250        self.con.putheader('Connection', 'close')
251        self.con.endheaders()
252        res = self.con.getresponse()
253        self.assertEqual(res.status, 501)
254
255    def test_head_keep_alive(self):
256        self.con._http_vsn_str = 'HTTP/1.1'
257        self.con.putrequest('GET', '/')
258        self.con.putheader('Connection', 'keep-alive')
259        self.con.endheaders()
260        res = self.con.getresponse()
261        self.assertEqual(res.status, 501)
262
263    def test_handler(self):
264        self.con.request('TEST', '/')
265        res = self.con.getresponse()
266        self.assertEqual(res.status, 204)
267
268    def test_return_header_keep_alive(self):
269        self.con.request('KEEP', '/')
270        res = self.con.getresponse()
271        self.assertEqual(res.getheader('Connection'), 'keep-alive')
272        self.con.request('TEST', '/')
273        self.addCleanup(self.con.close)
274
275    def test_internal_key_error(self):
276        self.con.request('KEYERROR', '/')
277        res = self.con.getresponse()
278        self.assertEqual(res.status, 999)
279
280    def test_return_custom_status(self):
281        self.con.request('CUSTOM', '/')
282        res = self.con.getresponse()
283        self.assertEqual(res.status, 999)
284
285    def test_send_error(self):
286        allow_transfer_encoding_codes = (205, 304)
287        for code in (101, 102, 204, 205, 304):
288            self.con.request('SEND_ERROR', '/{}'.format(code))
289            res = self.con.getresponse()
290            self.assertEqual(code, res.status)
291            self.assertEqual(None, res.getheader('Content-Length'))
292            self.assertEqual(None, res.getheader('Content-Type'))
293            if code not in allow_transfer_encoding_codes:
294                self.assertEqual(None, res.getheader('Transfer-Encoding'))
295
296            data = res.read()
297            self.assertEqual(b'', data)
298
299    def test_head_via_send_error(self):
300        allow_transfer_encoding_codes = (205, 304)
301        for code in (101, 200, 204, 205, 304):
302            self.con.request('HEAD', '/{}'.format(code))
303            res = self.con.getresponse()
304            self.assertEqual(code, res.status)
305            if code == 200:
306                self.assertEqual(None, res.getheader('Content-Length'))
307                self.assertIn('text/html', res.getheader('Content-Type'))
308            else:
309                self.assertEqual(None, res.getheader('Content-Length'))
310                self.assertEqual(None, res.getheader('Content-Type'))
311            if code not in allow_transfer_encoding_codes:
312                self.assertEqual(None, res.getheader('Transfer-Encoding'))
313
314            data = res.read()
315            self.assertEqual(b'', data)
316
317
318class SimpleHTTPServerTestCase(BaseTestCase):
319    class request_handler(NoLogRequestHandler, SimpleHTTPRequestHandler):
320        pass
321
322    def setUp(self):
323        BaseTestCase.setUp(self)
324        self.cwd = os.getcwd()
325        basetempdir = tempfile.gettempdir()
326        os.chdir(basetempdir)
327        self.data = 'We are the knights who say Ni!'
328        self.tempdir = tempfile.mkdtemp(dir=basetempdir)
329        self.tempdir_name = os.path.basename(self.tempdir)
330        self.base_url = '/' + self.tempdir_name
331        temp = open(os.path.join(self.tempdir, 'test'), 'wb')
332        temp.write(self.data)
333        temp.close()
334
335    def tearDown(self):
336        try:
337            os.chdir(self.cwd)
338            try:
339                shutil.rmtree(self.tempdir)
340            except OSError:
341                pass
342        finally:
343            BaseTestCase.tearDown(self)
344
345    def check_status_and_reason(self, response, status, data=None):
346        body = response.read()
347        self.assertTrue(response)
348        self.assertEqual(response.status, status)
349        self.assertIsNotNone(response.reason)
350        if data:
351            self.assertEqual(data, body)
352
353    def test_get(self):
354        #constructs the path relative to the root directory of the HTTPServer
355        response = self.request(self.base_url + '/test')
356        self.check_status_and_reason(response, 200, data=self.data)
357        # check for trailing "/" which should return 404. See Issue17324
358        response = self.request(self.base_url + '/test/')
359        self.check_status_and_reason(response, 404)
360        response = self.request(self.base_url + '/')
361        self.check_status_and_reason(response, 200)
362        response = self.request(self.base_url)
363        self.check_status_and_reason(response, 301)
364        response = self.request(self.base_url + '/?hi=2')
365        self.check_status_and_reason(response, 200)
366        response = self.request(self.base_url + '?hi=1')
367        self.check_status_and_reason(response, 301)
368        self.assertEqual(response.getheader("Location"),
369                         self.base_url + "/?hi=1")
370        response = self.request('/ThisDoesNotExist')
371        self.check_status_and_reason(response, 404)
372        response = self.request('/' + 'ThisDoesNotExist' + '/')
373        self.check_status_and_reason(response, 404)
374        with open(os.path.join(self.tempdir_name, 'index.html'), 'w') as fp:
375            response = self.request(self.base_url + '/')
376            self.check_status_and_reason(response, 200)
377            # chmod() doesn't work as expected on Windows, and filesystem
378            # permissions are ignored by root on Unix.
379            if os.name == 'posix' and os.geteuid() != 0:
380                os.chmod(self.tempdir, 0)
381                response = self.request(self.base_url + '/')
382                self.check_status_and_reason(response, 404)
383                os.chmod(self.tempdir, 0755)
384
385    def test_head(self):
386        response = self.request(
387            self.base_url + '/test', method='HEAD')
388        self.check_status_and_reason(response, 200)
389        self.assertEqual(response.getheader('content-length'),
390                         str(len(self.data)))
391        self.assertEqual(response.getheader('content-type'),
392                         'application/octet-stream')
393
394    def test_invalid_requests(self):
395        response = self.request('/', method='FOO')
396        self.check_status_and_reason(response, 501)
397        # requests must be case sensitive,so this should fail too
398        response = self.request('/', method='custom')
399        self.check_status_and_reason(response, 501)
400        response = self.request('/', method='GETs')
401        self.check_status_and_reason(response, 501)
402
403    def test_path_without_leading_slash(self):
404        response = self.request(self.tempdir_name + '/test')
405        self.check_status_and_reason(response, 200, data=self.data)
406        response = self.request(self.tempdir_name + '/test/')
407        self.check_status_and_reason(response, 404)
408        response = self.request(self.tempdir_name + '/')
409        self.check_status_and_reason(response, 200)
410        response = self.request(self.tempdir_name)
411        self.check_status_and_reason(response, 301)
412        response = self.request(self.tempdir_name + '/?hi=2')
413        self.check_status_and_reason(response, 200)
414        response = self.request(self.tempdir_name + '?hi=1')
415        self.check_status_and_reason(response, 301)
416        self.assertEqual(response.getheader("Location"),
417                         self.tempdir_name + "/?hi=1")
418
419
420cgi_file1 = """\
421#!%s
422
423print "Content-type: text/html"
424print
425print "Hello World"
426"""
427
428cgi_file2 = """\
429#!%s
430import cgi
431
432print "Content-type: text/html"
433print
434
435form = cgi.FieldStorage()
436print "%%s, %%s, %%s" %% (form.getfirst("spam"), form.getfirst("eggs"),
437                          form.getfirst("bacon"))
438"""
439
440cgi_file4 = """\
441#!%s
442import os
443
444print("Content-type: text/html")
445print("")
446
447print(os.environ["%s"])
448"""
449
450
451@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
452        "This test can't be run reliably as root (issue #13308).")
453class CGIHTTPServerTestCase(BaseTestCase):
454    class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler):
455        pass
456
457    def setUp(self):
458        BaseTestCase.setUp(self)
459        self.parent_dir = tempfile.mkdtemp()
460        self.cgi_dir = os.path.join(self.parent_dir, 'cgi-bin')
461        self.cgi_child_dir = os.path.join(self.cgi_dir, 'child-dir')
462        os.mkdir(self.cgi_dir)
463        os.mkdir(self.cgi_child_dir)
464
465        # The shebang line should be pure ASCII: use symlink if possible.
466        # See issue #7668.
467        if hasattr(os, 'symlink'):
468            self.pythonexe = os.path.join(self.parent_dir, 'python')
469            os.symlink(sys.executable, self.pythonexe)
470        else:
471            self.pythonexe = sys.executable
472
473        self.nocgi_path = os.path.join(self.parent_dir, 'nocgi.py')
474        with open(self.nocgi_path, 'w') as fp:
475            fp.write(cgi_file1 % self.pythonexe)
476        os.chmod(self.nocgi_path, 0777)
477
478        self.file1_path = os.path.join(self.cgi_dir, 'file1.py')
479        with open(self.file1_path, 'w') as file1:
480            file1.write(cgi_file1 % self.pythonexe)
481        os.chmod(self.file1_path, 0777)
482
483        self.file2_path = os.path.join(self.cgi_dir, 'file2.py')
484        with open(self.file2_path, 'w') as file2:
485            file2.write(cgi_file2 % self.pythonexe)
486        os.chmod(self.file2_path, 0777)
487
488        self.file3_path = os.path.join(self.cgi_child_dir, 'file3.py')
489        with open(self.file3_path, 'w') as file3:
490            file3.write(cgi_file1 % self.pythonexe)
491        os.chmod(self.file3_path, 0777)
492
493        self.file4_path = os.path.join(self.cgi_dir, 'file4.py')
494        with open(self.file4_path, 'w') as file4:
495            file4.write(cgi_file4 % (self.pythonexe, 'QUERY_STRING'))
496        os.chmod(self.file4_path, 0o777)
497
498        self.cwd = os.getcwd()
499        os.chdir(self.parent_dir)
500
501    def tearDown(self):
502        try:
503            os.chdir(self.cwd)
504            if self.pythonexe != sys.executable:
505                os.remove(self.pythonexe)
506            os.remove(self.nocgi_path)
507            os.remove(self.file1_path)
508            os.remove(self.file2_path)
509            os.remove(self.file3_path)
510            os.remove(self.file4_path)
511            os.rmdir(self.cgi_child_dir)
512            os.rmdir(self.cgi_dir)
513            os.rmdir(self.parent_dir)
514        finally:
515            BaseTestCase.tearDown(self)
516
517    def test_url_collapse_path(self):
518        # verify tail is the last portion and head is the rest on proper urls
519        test_vectors = {
520            '': '//',
521            '..': IndexError,
522            '/.//..': IndexError,
523            '/': '//',
524            '//': '//',
525            '/\\': '//\\',
526            '/.//': '//',
527            'cgi-bin/file1.py': '/cgi-bin/file1.py',
528            '/cgi-bin/file1.py': '/cgi-bin/file1.py',
529            'a': '//a',
530            '/a': '//a',
531            '//a': '//a',
532            './a': '//a',
533            './C:/': '/C:/',
534            '/a/b': '/a/b',
535            '/a/b/': '/a/b/',
536            '/a/b/.': '/a/b/',
537            '/a/b/c/..': '/a/b/',
538            '/a/b/c/../d': '/a/b/d',
539            '/a/b/c/../d/e/../f': '/a/b/d/f',
540            '/a/b/c/../d/e/../../f': '/a/b/f',
541            '/a/b/c/../d/e/.././././..//f': '/a/b/f',
542            '../a/b/c/../d/e/.././././..//f': IndexError,
543            '/a/b/c/../d/e/../../../f': '/a/f',
544            '/a/b/c/../d/e/../../../../f': '//f',
545            '/a/b/c/../d/e/../../../../../f': IndexError,
546            '/a/b/c/../d/e/../../../../f/..': '//',
547            '/a/b/c/../d/e/../../../../f/../.': '//',
548        }
549        for path, expected in test_vectors.iteritems():
550            if isinstance(expected, type) and issubclass(expected, Exception):
551                self.assertRaises(expected,
552                                  CGIHTTPServer._url_collapse_path, path)
553            else:
554                actual = CGIHTTPServer._url_collapse_path(path)
555                self.assertEqual(expected, actual,
556                                 msg='path = %r\nGot:    %r\nWanted: %r' %
557                                 (path, actual, expected))
558
559    def test_headers_and_content(self):
560        res = self.request('/cgi-bin/file1.py')
561        self.assertEqual(('Hello World\n', 'text/html', 200),
562            (res.read(), res.getheader('Content-type'), res.status))
563
564    def test_issue19435(self):
565        res = self.request('///////////nocgi.py/../cgi-bin/nothere.sh')
566        self.assertEqual(res.status, 404)
567
568    def test_post(self):
569        params = urllib.urlencode({'spam' : 1, 'eggs' : 'python', 'bacon' : 123456})
570        headers = {'Content-type' : 'application/x-www-form-urlencoded'}
571        res = self.request('/cgi-bin/file2.py', 'POST', params, headers)
572
573        self.assertEqual(res.read(), '1, python, 123456\n')
574
575    def test_invaliduri(self):
576        res = self.request('/cgi-bin/invalid')
577        res.read()
578        self.assertEqual(res.status, 404)
579
580    def test_authorization(self):
581        headers = {'Authorization' : 'Basic %s' %
582                   base64.b64encode('username:pass')}
583        res = self.request('/cgi-bin/file1.py', 'GET', headers=headers)
584        self.assertEqual(('Hello World\n', 'text/html', 200),
585                (res.read(), res.getheader('Content-type'), res.status))
586
587    def test_no_leading_slash(self):
588        # http://bugs.python.org/issue2254
589        res = self.request('cgi-bin/file1.py')
590        self.assertEqual(('Hello World\n', 'text/html', 200),
591             (res.read(), res.getheader('Content-type'), res.status))
592
593    def test_os_environ_is_not_altered(self):
594        signature = "Test CGI Server"
595        os.environ['SERVER_SOFTWARE'] = signature
596        res = self.request('/cgi-bin/file1.py')
597        self.assertEqual((b'Hello World\n', 'text/html', 200),
598                (res.read(), res.getheader('Content-type'), res.status))
599        self.assertEqual(os.environ['SERVER_SOFTWARE'], signature)
600
601    def test_urlquote_decoding_in_cgi_check(self):
602        res = self.request('/cgi-bin%2ffile1.py')
603        self.assertEqual((b'Hello World\n', 'text/html', 200),
604                (res.read(), res.getheader('Content-type'), res.status))
605
606    def test_nested_cgi_path_issue21323(self):
607        res = self.request('/cgi-bin/child-dir/file3.py')
608        self.assertEqual((b'Hello World\n', 'text/html', 200),
609                (res.read(), res.getheader('Content-type'), res.status))
610
611    def test_query_with_multiple_question_mark(self):
612        res = self.request('/cgi-bin/file4.py?a=b?c=d')
613        self.assertEqual(
614            (b'a=b?c=d\n', 'text/html', 200),
615            (res.read(), res.getheader('Content-type'), res.status))
616
617    def test_query_with_continuous_slashes(self):
618        res = self.request('/cgi-bin/file4.py?k=aa%2F%2Fbb&//q//p//=//a//b//')
619        self.assertEqual(
620            (b'k=aa%2F%2Fbb&//q//p//=//a//b//\n',
621             'text/html', 200),
622            (res.read(), res.getheader('Content-type'), res.status))
623
624
625class SimpleHTTPRequestHandlerTestCase(unittest.TestCase):
626    """ Test url parsing """
627    def setUp(self):
628        self.translated = os.getcwd()
629        self.translated = os.path.join(self.translated, 'filename')
630        self.handler = SocketlessRequestHandler()
631
632    def test_query_arguments(self):
633        path = self.handler.translate_path('/filename')
634        self.assertEqual(path, self.translated)
635        path = self.handler.translate_path('/filename?foo=bar')
636        self.assertEqual(path, self.translated)
637        path = self.handler.translate_path('/filename?a=b&spam=eggs#zot')
638        self.assertEqual(path, self.translated)
639
640    def test_start_with_double_slash(self):
641        path = self.handler.translate_path('//filename')
642        self.assertEqual(path, self.translated)
643        path = self.handler.translate_path('//filename?foo=bar')
644        self.assertEqual(path, self.translated)
645
646    def test_windows_colon(self):
647        import SimpleHTTPServer
648        with test_support.swap_attr(SimpleHTTPServer.os, 'path', ntpath):
649            path = self.handler.translate_path('c:c:c:foo/filename')
650            path = path.replace(ntpath.sep, os.sep)
651            self.assertEqual(path, self.translated)
652
653            path = self.handler.translate_path('\\c:../filename')
654            path = path.replace(ntpath.sep, os.sep)
655            self.assertEqual(path, self.translated)
656
657            path = self.handler.translate_path('c:\\c:..\\foo/filename')
658            path = path.replace(ntpath.sep, os.sep)
659            self.assertEqual(path, self.translated)
660
661            path = self.handler.translate_path('c:c:foo\\c:c:bar/filename')
662            path = path.replace(ntpath.sep, os.sep)
663            self.assertEqual(path, self.translated)
664
665
666def test_main(verbose=None):
667    try:
668        cwd = os.getcwd()
669        test_support.run_unittest(BaseHTTPRequestHandlerTestCase,
670                                  SimpleHTTPRequestHandlerTestCase,
671                                  BaseHTTPServerTestCase,
672                                  SimpleHTTPServerTestCase,
673                                  CGIHTTPServerTestCase
674                                 )
675    finally:
676        os.chdir(cwd)
677
678if __name__ == '__main__':
679    test_main()
680