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