17db96d56Sopenharmony_ci"""Unittests for the various HTTPServer modules. 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciWritten by Cody A.W. Somerville <cody-somerville@ubuntu.com>, 47db96d56Sopenharmony_ciJosip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest. 57db96d56Sopenharmony_ci""" 67db96d56Sopenharmony_cifrom collections import OrderedDict 77db96d56Sopenharmony_cifrom http.server import BaseHTTPRequestHandler, HTTPServer, \ 87db96d56Sopenharmony_ci SimpleHTTPRequestHandler, CGIHTTPRequestHandler 97db96d56Sopenharmony_cifrom http import server, HTTPStatus 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ciimport os 127db96d56Sopenharmony_ciimport socket 137db96d56Sopenharmony_ciimport sys 147db96d56Sopenharmony_ciimport re 157db96d56Sopenharmony_ciimport base64 167db96d56Sopenharmony_ciimport ntpath 177db96d56Sopenharmony_ciimport pathlib 187db96d56Sopenharmony_ciimport shutil 197db96d56Sopenharmony_ciimport email.message 207db96d56Sopenharmony_ciimport email.utils 217db96d56Sopenharmony_ciimport html 227db96d56Sopenharmony_ciimport http, http.client 237db96d56Sopenharmony_ciimport urllib.parse 247db96d56Sopenharmony_ciimport tempfile 257db96d56Sopenharmony_ciimport time 267db96d56Sopenharmony_ciimport datetime 277db96d56Sopenharmony_ciimport threading 287db96d56Sopenharmony_cifrom unittest import mock 297db96d56Sopenharmony_cifrom io import BytesIO, StringIO 307db96d56Sopenharmony_ci 317db96d56Sopenharmony_ciimport unittest 327db96d56Sopenharmony_cifrom test import support 337db96d56Sopenharmony_cifrom test.support import os_helper 347db96d56Sopenharmony_cifrom test.support import threading_helper 357db96d56Sopenharmony_ci 367db96d56Sopenharmony_cisupport.requires_working_socket(module=True) 377db96d56Sopenharmony_ci 387db96d56Sopenharmony_ciclass NoLogRequestHandler: 397db96d56Sopenharmony_ci def log_message(self, *args): 407db96d56Sopenharmony_ci # don't write log messages to stderr 417db96d56Sopenharmony_ci pass 427db96d56Sopenharmony_ci 437db96d56Sopenharmony_ci def read(self, n=None): 447db96d56Sopenharmony_ci return '' 457db96d56Sopenharmony_ci 467db96d56Sopenharmony_ci 477db96d56Sopenharmony_ciclass TestServerThread(threading.Thread): 487db96d56Sopenharmony_ci def __init__(self, test_object, request_handler): 497db96d56Sopenharmony_ci threading.Thread.__init__(self) 507db96d56Sopenharmony_ci self.request_handler = request_handler 517db96d56Sopenharmony_ci self.test_object = test_object 527db96d56Sopenharmony_ci 537db96d56Sopenharmony_ci def run(self): 547db96d56Sopenharmony_ci self.server = HTTPServer(('localhost', 0), self.request_handler) 557db96d56Sopenharmony_ci self.test_object.HOST, self.test_object.PORT = self.server.socket.getsockname() 567db96d56Sopenharmony_ci self.test_object.server_started.set() 577db96d56Sopenharmony_ci self.test_object = None 587db96d56Sopenharmony_ci try: 597db96d56Sopenharmony_ci self.server.serve_forever(0.05) 607db96d56Sopenharmony_ci finally: 617db96d56Sopenharmony_ci self.server.server_close() 627db96d56Sopenharmony_ci 637db96d56Sopenharmony_ci def stop(self): 647db96d56Sopenharmony_ci self.server.shutdown() 657db96d56Sopenharmony_ci self.join() 667db96d56Sopenharmony_ci 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_ciclass BaseTestCase(unittest.TestCase): 697db96d56Sopenharmony_ci def setUp(self): 707db96d56Sopenharmony_ci self._threads = threading_helper.threading_setup() 717db96d56Sopenharmony_ci os.environ = os_helper.EnvironmentVarGuard() 727db96d56Sopenharmony_ci self.server_started = threading.Event() 737db96d56Sopenharmony_ci self.thread = TestServerThread(self, self.request_handler) 747db96d56Sopenharmony_ci self.thread.start() 757db96d56Sopenharmony_ci self.server_started.wait() 767db96d56Sopenharmony_ci 777db96d56Sopenharmony_ci def tearDown(self): 787db96d56Sopenharmony_ci self.thread.stop() 797db96d56Sopenharmony_ci self.thread = None 807db96d56Sopenharmony_ci os.environ.__exit__() 817db96d56Sopenharmony_ci threading_helper.threading_cleanup(*self._threads) 827db96d56Sopenharmony_ci 837db96d56Sopenharmony_ci def request(self, uri, method='GET', body=None, headers={}): 847db96d56Sopenharmony_ci self.connection = http.client.HTTPConnection(self.HOST, self.PORT) 857db96d56Sopenharmony_ci self.connection.request(method, uri, body, headers) 867db96d56Sopenharmony_ci return self.connection.getresponse() 877db96d56Sopenharmony_ci 887db96d56Sopenharmony_ci 897db96d56Sopenharmony_ciclass BaseHTTPServerTestCase(BaseTestCase): 907db96d56Sopenharmony_ci class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler): 917db96d56Sopenharmony_ci protocol_version = 'HTTP/1.1' 927db96d56Sopenharmony_ci default_request_version = 'HTTP/1.1' 937db96d56Sopenharmony_ci 947db96d56Sopenharmony_ci def do_TEST(self): 957db96d56Sopenharmony_ci self.send_response(HTTPStatus.NO_CONTENT) 967db96d56Sopenharmony_ci self.send_header('Content-Type', 'text/html') 977db96d56Sopenharmony_ci self.send_header('Connection', 'close') 987db96d56Sopenharmony_ci self.end_headers() 997db96d56Sopenharmony_ci 1007db96d56Sopenharmony_ci def do_KEEP(self): 1017db96d56Sopenharmony_ci self.send_response(HTTPStatus.NO_CONTENT) 1027db96d56Sopenharmony_ci self.send_header('Content-Type', 'text/html') 1037db96d56Sopenharmony_ci self.send_header('Connection', 'keep-alive') 1047db96d56Sopenharmony_ci self.end_headers() 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_ci def do_KEYERROR(self): 1077db96d56Sopenharmony_ci self.send_error(999) 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci def do_NOTFOUND(self): 1107db96d56Sopenharmony_ci self.send_error(HTTPStatus.NOT_FOUND) 1117db96d56Sopenharmony_ci 1127db96d56Sopenharmony_ci def do_EXPLAINERROR(self): 1137db96d56Sopenharmony_ci self.send_error(999, "Short Message", 1147db96d56Sopenharmony_ci "This is a long \n explanation") 1157db96d56Sopenharmony_ci 1167db96d56Sopenharmony_ci def do_CUSTOM(self): 1177db96d56Sopenharmony_ci self.send_response(999) 1187db96d56Sopenharmony_ci self.send_header('Content-Type', 'text/html') 1197db96d56Sopenharmony_ci self.send_header('Connection', 'close') 1207db96d56Sopenharmony_ci self.end_headers() 1217db96d56Sopenharmony_ci 1227db96d56Sopenharmony_ci def do_LATINONEHEADER(self): 1237db96d56Sopenharmony_ci self.send_response(999) 1247db96d56Sopenharmony_ci self.send_header('X-Special', 'Dängerous Mind') 1257db96d56Sopenharmony_ci self.send_header('Connection', 'close') 1267db96d56Sopenharmony_ci self.end_headers() 1277db96d56Sopenharmony_ci body = self.headers['x-special-incoming'].encode('utf-8') 1287db96d56Sopenharmony_ci self.wfile.write(body) 1297db96d56Sopenharmony_ci 1307db96d56Sopenharmony_ci def do_SEND_ERROR(self): 1317db96d56Sopenharmony_ci self.send_error(int(self.path[1:])) 1327db96d56Sopenharmony_ci 1337db96d56Sopenharmony_ci def do_HEAD(self): 1347db96d56Sopenharmony_ci self.send_error(int(self.path[1:])) 1357db96d56Sopenharmony_ci 1367db96d56Sopenharmony_ci def setUp(self): 1377db96d56Sopenharmony_ci BaseTestCase.setUp(self) 1387db96d56Sopenharmony_ci self.con = http.client.HTTPConnection(self.HOST, self.PORT) 1397db96d56Sopenharmony_ci self.con.connect() 1407db96d56Sopenharmony_ci 1417db96d56Sopenharmony_ci def test_command(self): 1427db96d56Sopenharmony_ci self.con.request('GET', '/') 1437db96d56Sopenharmony_ci res = self.con.getresponse() 1447db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) 1457db96d56Sopenharmony_ci 1467db96d56Sopenharmony_ci def test_request_line_trimming(self): 1477db96d56Sopenharmony_ci self.con._http_vsn_str = 'HTTP/1.1\n' 1487db96d56Sopenharmony_ci self.con.putrequest('XYZBOGUS', '/') 1497db96d56Sopenharmony_ci self.con.endheaders() 1507db96d56Sopenharmony_ci res = self.con.getresponse() 1517db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) 1527db96d56Sopenharmony_ci 1537db96d56Sopenharmony_ci def test_version_bogus(self): 1547db96d56Sopenharmony_ci self.con._http_vsn_str = 'FUBAR' 1557db96d56Sopenharmony_ci self.con.putrequest('GET', '/') 1567db96d56Sopenharmony_ci self.con.endheaders() 1577db96d56Sopenharmony_ci res = self.con.getresponse() 1587db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) 1597db96d56Sopenharmony_ci 1607db96d56Sopenharmony_ci def test_version_digits(self): 1617db96d56Sopenharmony_ci self.con._http_vsn_str = 'HTTP/9.9.9' 1627db96d56Sopenharmony_ci self.con.putrequest('GET', '/') 1637db96d56Sopenharmony_ci self.con.endheaders() 1647db96d56Sopenharmony_ci res = self.con.getresponse() 1657db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) 1667db96d56Sopenharmony_ci 1677db96d56Sopenharmony_ci def test_version_signs_and_underscores(self): 1687db96d56Sopenharmony_ci self.con._http_vsn_str = 'HTTP/-9_9_9.+9_9_9' 1697db96d56Sopenharmony_ci self.con.putrequest('GET', '/') 1707db96d56Sopenharmony_ci self.con.endheaders() 1717db96d56Sopenharmony_ci res = self.con.getresponse() 1727db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) 1737db96d56Sopenharmony_ci 1747db96d56Sopenharmony_ci def test_major_version_number_too_long(self): 1757db96d56Sopenharmony_ci self.con._http_vsn_str = 'HTTP/909876543210.0' 1767db96d56Sopenharmony_ci self.con.putrequest('GET', '/') 1777db96d56Sopenharmony_ci self.con.endheaders() 1787db96d56Sopenharmony_ci res = self.con.getresponse() 1797db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) 1807db96d56Sopenharmony_ci 1817db96d56Sopenharmony_ci def test_minor_version_number_too_long(self): 1827db96d56Sopenharmony_ci self.con._http_vsn_str = 'HTTP/1.909876543210' 1837db96d56Sopenharmony_ci self.con.putrequest('GET', '/') 1847db96d56Sopenharmony_ci self.con.endheaders() 1857db96d56Sopenharmony_ci res = self.con.getresponse() 1867db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci def test_version_none_get(self): 1897db96d56Sopenharmony_ci self.con._http_vsn_str = '' 1907db96d56Sopenharmony_ci self.con.putrequest('GET', '/') 1917db96d56Sopenharmony_ci self.con.endheaders() 1927db96d56Sopenharmony_ci res = self.con.getresponse() 1937db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) 1947db96d56Sopenharmony_ci 1957db96d56Sopenharmony_ci def test_version_none(self): 1967db96d56Sopenharmony_ci # Test that a valid method is rejected when not HTTP/1.x 1977db96d56Sopenharmony_ci self.con._http_vsn_str = '' 1987db96d56Sopenharmony_ci self.con.putrequest('CUSTOM', '/') 1997db96d56Sopenharmony_ci self.con.endheaders() 2007db96d56Sopenharmony_ci res = self.con.getresponse() 2017db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) 2027db96d56Sopenharmony_ci 2037db96d56Sopenharmony_ci def test_version_invalid(self): 2047db96d56Sopenharmony_ci self.con._http_vsn = 99 2057db96d56Sopenharmony_ci self.con._http_vsn_str = 'HTTP/9.9' 2067db96d56Sopenharmony_ci self.con.putrequest('GET', '/') 2077db96d56Sopenharmony_ci self.con.endheaders() 2087db96d56Sopenharmony_ci res = self.con.getresponse() 2097db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.HTTP_VERSION_NOT_SUPPORTED) 2107db96d56Sopenharmony_ci 2117db96d56Sopenharmony_ci def test_send_blank(self): 2127db96d56Sopenharmony_ci self.con._http_vsn_str = '' 2137db96d56Sopenharmony_ci self.con.putrequest('', '') 2147db96d56Sopenharmony_ci self.con.endheaders() 2157db96d56Sopenharmony_ci res = self.con.getresponse() 2167db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) 2177db96d56Sopenharmony_ci 2187db96d56Sopenharmony_ci def test_header_close(self): 2197db96d56Sopenharmony_ci self.con.putrequest('GET', '/') 2207db96d56Sopenharmony_ci self.con.putheader('Connection', 'close') 2217db96d56Sopenharmony_ci self.con.endheaders() 2227db96d56Sopenharmony_ci res = self.con.getresponse() 2237db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) 2247db96d56Sopenharmony_ci 2257db96d56Sopenharmony_ci def test_header_keep_alive(self): 2267db96d56Sopenharmony_ci self.con._http_vsn_str = 'HTTP/1.1' 2277db96d56Sopenharmony_ci self.con.putrequest('GET', '/') 2287db96d56Sopenharmony_ci self.con.putheader('Connection', 'keep-alive') 2297db96d56Sopenharmony_ci self.con.endheaders() 2307db96d56Sopenharmony_ci res = self.con.getresponse() 2317db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) 2327db96d56Sopenharmony_ci 2337db96d56Sopenharmony_ci def test_handler(self): 2347db96d56Sopenharmony_ci self.con.request('TEST', '/') 2357db96d56Sopenharmony_ci res = self.con.getresponse() 2367db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.NO_CONTENT) 2377db96d56Sopenharmony_ci 2387db96d56Sopenharmony_ci def test_return_header_keep_alive(self): 2397db96d56Sopenharmony_ci self.con.request('KEEP', '/') 2407db96d56Sopenharmony_ci res = self.con.getresponse() 2417db96d56Sopenharmony_ci self.assertEqual(res.getheader('Connection'), 'keep-alive') 2427db96d56Sopenharmony_ci self.con.request('TEST', '/') 2437db96d56Sopenharmony_ci self.addCleanup(self.con.close) 2447db96d56Sopenharmony_ci 2457db96d56Sopenharmony_ci def test_internal_key_error(self): 2467db96d56Sopenharmony_ci self.con.request('KEYERROR', '/') 2477db96d56Sopenharmony_ci res = self.con.getresponse() 2487db96d56Sopenharmony_ci self.assertEqual(res.status, 999) 2497db96d56Sopenharmony_ci 2507db96d56Sopenharmony_ci def test_return_custom_status(self): 2517db96d56Sopenharmony_ci self.con.request('CUSTOM', '/') 2527db96d56Sopenharmony_ci res = self.con.getresponse() 2537db96d56Sopenharmony_ci self.assertEqual(res.status, 999) 2547db96d56Sopenharmony_ci 2557db96d56Sopenharmony_ci def test_return_explain_error(self): 2567db96d56Sopenharmony_ci self.con.request('EXPLAINERROR', '/') 2577db96d56Sopenharmony_ci res = self.con.getresponse() 2587db96d56Sopenharmony_ci self.assertEqual(res.status, 999) 2597db96d56Sopenharmony_ci self.assertTrue(int(res.getheader('Content-Length'))) 2607db96d56Sopenharmony_ci 2617db96d56Sopenharmony_ci def test_latin1_header(self): 2627db96d56Sopenharmony_ci self.con.request('LATINONEHEADER', '/', headers={ 2637db96d56Sopenharmony_ci 'X-Special-Incoming': 'Ärger mit Unicode' 2647db96d56Sopenharmony_ci }) 2657db96d56Sopenharmony_ci res = self.con.getresponse() 2667db96d56Sopenharmony_ci self.assertEqual(res.getheader('X-Special'), 'Dängerous Mind') 2677db96d56Sopenharmony_ci self.assertEqual(res.read(), 'Ärger mit Unicode'.encode('utf-8')) 2687db96d56Sopenharmony_ci 2697db96d56Sopenharmony_ci def test_error_content_length(self): 2707db96d56Sopenharmony_ci # Issue #16088: standard error responses should have a content-length 2717db96d56Sopenharmony_ci self.con.request('NOTFOUND', '/') 2727db96d56Sopenharmony_ci res = self.con.getresponse() 2737db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.NOT_FOUND) 2747db96d56Sopenharmony_ci 2757db96d56Sopenharmony_ci data = res.read() 2767db96d56Sopenharmony_ci self.assertEqual(int(res.getheader('Content-Length')), len(data)) 2777db96d56Sopenharmony_ci 2787db96d56Sopenharmony_ci def test_send_error(self): 2797db96d56Sopenharmony_ci allow_transfer_encoding_codes = (HTTPStatus.NOT_MODIFIED, 2807db96d56Sopenharmony_ci HTTPStatus.RESET_CONTENT) 2817db96d56Sopenharmony_ci for code in (HTTPStatus.NO_CONTENT, HTTPStatus.NOT_MODIFIED, 2827db96d56Sopenharmony_ci HTTPStatus.PROCESSING, HTTPStatus.RESET_CONTENT, 2837db96d56Sopenharmony_ci HTTPStatus.SWITCHING_PROTOCOLS): 2847db96d56Sopenharmony_ci self.con.request('SEND_ERROR', '/{}'.format(code)) 2857db96d56Sopenharmony_ci res = self.con.getresponse() 2867db96d56Sopenharmony_ci self.assertEqual(code, res.status) 2877db96d56Sopenharmony_ci self.assertEqual(None, res.getheader('Content-Length')) 2887db96d56Sopenharmony_ci self.assertEqual(None, res.getheader('Content-Type')) 2897db96d56Sopenharmony_ci if code not in allow_transfer_encoding_codes: 2907db96d56Sopenharmony_ci self.assertEqual(None, res.getheader('Transfer-Encoding')) 2917db96d56Sopenharmony_ci 2927db96d56Sopenharmony_ci data = res.read() 2937db96d56Sopenharmony_ci self.assertEqual(b'', data) 2947db96d56Sopenharmony_ci 2957db96d56Sopenharmony_ci def test_head_via_send_error(self): 2967db96d56Sopenharmony_ci allow_transfer_encoding_codes = (HTTPStatus.NOT_MODIFIED, 2977db96d56Sopenharmony_ci HTTPStatus.RESET_CONTENT) 2987db96d56Sopenharmony_ci for code in (HTTPStatus.OK, HTTPStatus.NO_CONTENT, 2997db96d56Sopenharmony_ci HTTPStatus.NOT_MODIFIED, HTTPStatus.RESET_CONTENT, 3007db96d56Sopenharmony_ci HTTPStatus.SWITCHING_PROTOCOLS): 3017db96d56Sopenharmony_ci self.con.request('HEAD', '/{}'.format(code)) 3027db96d56Sopenharmony_ci res = self.con.getresponse() 3037db96d56Sopenharmony_ci self.assertEqual(code, res.status) 3047db96d56Sopenharmony_ci if code == HTTPStatus.OK: 3057db96d56Sopenharmony_ci self.assertTrue(int(res.getheader('Content-Length')) > 0) 3067db96d56Sopenharmony_ci self.assertIn('text/html', res.getheader('Content-Type')) 3077db96d56Sopenharmony_ci else: 3087db96d56Sopenharmony_ci self.assertEqual(None, res.getheader('Content-Length')) 3097db96d56Sopenharmony_ci self.assertEqual(None, res.getheader('Content-Type')) 3107db96d56Sopenharmony_ci if code not in allow_transfer_encoding_codes: 3117db96d56Sopenharmony_ci self.assertEqual(None, res.getheader('Transfer-Encoding')) 3127db96d56Sopenharmony_ci 3137db96d56Sopenharmony_ci data = res.read() 3147db96d56Sopenharmony_ci self.assertEqual(b'', data) 3157db96d56Sopenharmony_ci 3167db96d56Sopenharmony_ci 3177db96d56Sopenharmony_ciclass RequestHandlerLoggingTestCase(BaseTestCase): 3187db96d56Sopenharmony_ci class request_handler(BaseHTTPRequestHandler): 3197db96d56Sopenharmony_ci protocol_version = 'HTTP/1.1' 3207db96d56Sopenharmony_ci default_request_version = 'HTTP/1.1' 3217db96d56Sopenharmony_ci 3227db96d56Sopenharmony_ci def do_GET(self): 3237db96d56Sopenharmony_ci self.send_response(HTTPStatus.OK) 3247db96d56Sopenharmony_ci self.end_headers() 3257db96d56Sopenharmony_ci 3267db96d56Sopenharmony_ci def do_ERROR(self): 3277db96d56Sopenharmony_ci self.send_error(HTTPStatus.NOT_FOUND, 'File not found') 3287db96d56Sopenharmony_ci 3297db96d56Sopenharmony_ci def test_get(self): 3307db96d56Sopenharmony_ci self.con = http.client.HTTPConnection(self.HOST, self.PORT) 3317db96d56Sopenharmony_ci self.con.connect() 3327db96d56Sopenharmony_ci 3337db96d56Sopenharmony_ci with support.captured_stderr() as err: 3347db96d56Sopenharmony_ci self.con.request('GET', '/') 3357db96d56Sopenharmony_ci self.con.getresponse() 3367db96d56Sopenharmony_ci 3377db96d56Sopenharmony_ci self.assertTrue( 3387db96d56Sopenharmony_ci err.getvalue().endswith('"GET / HTTP/1.1" 200 -\n')) 3397db96d56Sopenharmony_ci 3407db96d56Sopenharmony_ci def test_err(self): 3417db96d56Sopenharmony_ci self.con = http.client.HTTPConnection(self.HOST, self.PORT) 3427db96d56Sopenharmony_ci self.con.connect() 3437db96d56Sopenharmony_ci 3447db96d56Sopenharmony_ci with support.captured_stderr() as err: 3457db96d56Sopenharmony_ci self.con.request('ERROR', '/') 3467db96d56Sopenharmony_ci self.con.getresponse() 3477db96d56Sopenharmony_ci 3487db96d56Sopenharmony_ci lines = err.getvalue().split('\n') 3497db96d56Sopenharmony_ci self.assertTrue(lines[0].endswith('code 404, message File not found')) 3507db96d56Sopenharmony_ci self.assertTrue(lines[1].endswith('"ERROR / HTTP/1.1" 404 -')) 3517db96d56Sopenharmony_ci 3527db96d56Sopenharmony_ci 3537db96d56Sopenharmony_ciclass SimpleHTTPServerTestCase(BaseTestCase): 3547db96d56Sopenharmony_ci class request_handler(NoLogRequestHandler, SimpleHTTPRequestHandler): 3557db96d56Sopenharmony_ci pass 3567db96d56Sopenharmony_ci 3577db96d56Sopenharmony_ci def setUp(self): 3587db96d56Sopenharmony_ci super().setUp() 3597db96d56Sopenharmony_ci self.cwd = os.getcwd() 3607db96d56Sopenharmony_ci basetempdir = tempfile.gettempdir() 3617db96d56Sopenharmony_ci os.chdir(basetempdir) 3627db96d56Sopenharmony_ci self.data = b'We are the knights who say Ni!' 3637db96d56Sopenharmony_ci self.tempdir = tempfile.mkdtemp(dir=basetempdir) 3647db96d56Sopenharmony_ci self.tempdir_name = os.path.basename(self.tempdir) 3657db96d56Sopenharmony_ci self.base_url = '/' + self.tempdir_name 3667db96d56Sopenharmony_ci tempname = os.path.join(self.tempdir, 'test') 3677db96d56Sopenharmony_ci with open(tempname, 'wb') as temp: 3687db96d56Sopenharmony_ci temp.write(self.data) 3697db96d56Sopenharmony_ci temp.flush() 3707db96d56Sopenharmony_ci mtime = os.stat(tempname).st_mtime 3717db96d56Sopenharmony_ci # compute last modification datetime for browser cache tests 3727db96d56Sopenharmony_ci last_modif = datetime.datetime.fromtimestamp(mtime, 3737db96d56Sopenharmony_ci datetime.timezone.utc) 3747db96d56Sopenharmony_ci self.last_modif_datetime = last_modif.replace(microsecond=0) 3757db96d56Sopenharmony_ci self.last_modif_header = email.utils.formatdate( 3767db96d56Sopenharmony_ci last_modif.timestamp(), usegmt=True) 3777db96d56Sopenharmony_ci 3787db96d56Sopenharmony_ci def tearDown(self): 3797db96d56Sopenharmony_ci try: 3807db96d56Sopenharmony_ci os.chdir(self.cwd) 3817db96d56Sopenharmony_ci try: 3827db96d56Sopenharmony_ci shutil.rmtree(self.tempdir) 3837db96d56Sopenharmony_ci except: 3847db96d56Sopenharmony_ci pass 3857db96d56Sopenharmony_ci finally: 3867db96d56Sopenharmony_ci super().tearDown() 3877db96d56Sopenharmony_ci 3887db96d56Sopenharmony_ci def check_status_and_reason(self, response, status, data=None): 3897db96d56Sopenharmony_ci def close_conn(): 3907db96d56Sopenharmony_ci """Don't close reader yet so we can check if there was leftover 3917db96d56Sopenharmony_ci buffered input""" 3927db96d56Sopenharmony_ci nonlocal reader 3937db96d56Sopenharmony_ci reader = response.fp 3947db96d56Sopenharmony_ci response.fp = None 3957db96d56Sopenharmony_ci reader = None 3967db96d56Sopenharmony_ci response._close_conn = close_conn 3977db96d56Sopenharmony_ci 3987db96d56Sopenharmony_ci body = response.read() 3997db96d56Sopenharmony_ci self.assertTrue(response) 4007db96d56Sopenharmony_ci self.assertEqual(response.status, status) 4017db96d56Sopenharmony_ci self.assertIsNotNone(response.reason) 4027db96d56Sopenharmony_ci if data: 4037db96d56Sopenharmony_ci self.assertEqual(data, body) 4047db96d56Sopenharmony_ci # Ensure the server has not set up a persistent connection, and has 4057db96d56Sopenharmony_ci # not sent any extra data 4067db96d56Sopenharmony_ci self.assertEqual(response.version, 10) 4077db96d56Sopenharmony_ci self.assertEqual(response.msg.get("Connection", "close"), "close") 4087db96d56Sopenharmony_ci self.assertEqual(reader.read(30), b'', 'Connection should be closed') 4097db96d56Sopenharmony_ci 4107db96d56Sopenharmony_ci reader.close() 4117db96d56Sopenharmony_ci return body 4127db96d56Sopenharmony_ci 4137db96d56Sopenharmony_ci @unittest.skipIf(sys.platform == 'darwin', 4147db96d56Sopenharmony_ci 'undecodable name cannot always be decoded on macOS') 4157db96d56Sopenharmony_ci @unittest.skipIf(sys.platform == 'win32', 4167db96d56Sopenharmony_ci 'undecodable name cannot be decoded on win32') 4177db96d56Sopenharmony_ci @unittest.skipUnless(os_helper.TESTFN_UNDECODABLE, 4187db96d56Sopenharmony_ci 'need os_helper.TESTFN_UNDECODABLE') 4197db96d56Sopenharmony_ci def test_undecodable_filename(self): 4207db96d56Sopenharmony_ci enc = sys.getfilesystemencoding() 4217db96d56Sopenharmony_ci filename = os.fsdecode(os_helper.TESTFN_UNDECODABLE) + '.txt' 4227db96d56Sopenharmony_ci with open(os.path.join(self.tempdir, filename), 'wb') as f: 4237db96d56Sopenharmony_ci f.write(os_helper.TESTFN_UNDECODABLE) 4247db96d56Sopenharmony_ci response = self.request(self.base_url + '/') 4257db96d56Sopenharmony_ci if sys.platform == 'darwin': 4267db96d56Sopenharmony_ci # On Mac OS the HFS+ filesystem replaces bytes that aren't valid 4277db96d56Sopenharmony_ci # UTF-8 into a percent-encoded value. 4287db96d56Sopenharmony_ci for name in os.listdir(self.tempdir): 4297db96d56Sopenharmony_ci if name != 'test': # Ignore a filename created in setUp(). 4307db96d56Sopenharmony_ci filename = name 4317db96d56Sopenharmony_ci break 4327db96d56Sopenharmony_ci body = self.check_status_and_reason(response, HTTPStatus.OK) 4337db96d56Sopenharmony_ci quotedname = urllib.parse.quote(filename, errors='surrogatepass') 4347db96d56Sopenharmony_ci self.assertIn(('href="%s"' % quotedname) 4357db96d56Sopenharmony_ci .encode(enc, 'surrogateescape'), body) 4367db96d56Sopenharmony_ci self.assertIn(('>%s<' % html.escape(filename, quote=False)) 4377db96d56Sopenharmony_ci .encode(enc, 'surrogateescape'), body) 4387db96d56Sopenharmony_ci response = self.request(self.base_url + '/' + quotedname) 4397db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK, 4407db96d56Sopenharmony_ci data=os_helper.TESTFN_UNDECODABLE) 4417db96d56Sopenharmony_ci 4427db96d56Sopenharmony_ci def test_undecodable_parameter(self): 4437db96d56Sopenharmony_ci # sanity check using a valid parameter 4447db96d56Sopenharmony_ci response = self.request(self.base_url + '/?x=123').read() 4457db96d56Sopenharmony_ci self.assertRegex(response, f'listing for {self.base_url}/\?x=123'.encode('latin1')) 4467db96d56Sopenharmony_ci # now the bogus encoding 4477db96d56Sopenharmony_ci response = self.request(self.base_url + '/?x=%bb').read() 4487db96d56Sopenharmony_ci self.assertRegex(response, f'listing for {self.base_url}/\?x=\xef\xbf\xbd'.encode('latin1')) 4497db96d56Sopenharmony_ci 4507db96d56Sopenharmony_ci def test_get_dir_redirect_location_domain_injection_bug(self): 4517db96d56Sopenharmony_ci """Ensure //evil.co/..%2f../../X does not put //evil.co/ in Location. 4527db96d56Sopenharmony_ci 4537db96d56Sopenharmony_ci //netloc/ in a Location header is a redirect to a new host. 4547db96d56Sopenharmony_ci https://github.com/python/cpython/issues/87389 4557db96d56Sopenharmony_ci 4567db96d56Sopenharmony_ci This checks that a path resolving to a directory on our server cannot 4577db96d56Sopenharmony_ci resolve into a redirect to another server. 4587db96d56Sopenharmony_ci """ 4597db96d56Sopenharmony_ci os.mkdir(os.path.join(self.tempdir, 'existing_directory')) 4607db96d56Sopenharmony_ci url = f'/python.org/..%2f..%2f..%2f..%2f..%2f../%0a%0d/../{self.tempdir_name}/existing_directory' 4617db96d56Sopenharmony_ci expected_location = f'{url}/' # /python.org.../ single slash single prefix, trailing slash 4627db96d56Sopenharmony_ci # Canonicalizes to /tmp/tempdir_name/existing_directory which does 4637db96d56Sopenharmony_ci # exist and is a dir, triggering the 301 redirect logic. 4647db96d56Sopenharmony_ci response = self.request(url) 4657db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) 4667db96d56Sopenharmony_ci location = response.getheader('Location') 4677db96d56Sopenharmony_ci self.assertEqual(location, expected_location, msg='non-attack failed!') 4687db96d56Sopenharmony_ci 4697db96d56Sopenharmony_ci # //python.org... multi-slash prefix, no trailing slash 4707db96d56Sopenharmony_ci attack_url = f'/{url}' 4717db96d56Sopenharmony_ci response = self.request(attack_url) 4727db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) 4737db96d56Sopenharmony_ci location = response.getheader('Location') 4747db96d56Sopenharmony_ci self.assertFalse(location.startswith('//'), msg=location) 4757db96d56Sopenharmony_ci self.assertEqual(location, expected_location, 4767db96d56Sopenharmony_ci msg='Expected Location header to start with a single / and ' 4777db96d56Sopenharmony_ci 'end with a / as this is a directory redirect.') 4787db96d56Sopenharmony_ci 4797db96d56Sopenharmony_ci # ///python.org... triple-slash prefix, no trailing slash 4807db96d56Sopenharmony_ci attack3_url = f'//{url}' 4817db96d56Sopenharmony_ci response = self.request(attack3_url) 4827db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) 4837db96d56Sopenharmony_ci self.assertEqual(response.getheader('Location'), expected_location) 4847db96d56Sopenharmony_ci 4857db96d56Sopenharmony_ci # If the second word in the http request (Request-URI for the http 4867db96d56Sopenharmony_ci # method) is a full URI, we don't worry about it, as that'll be parsed 4877db96d56Sopenharmony_ci # and reassembled as a full URI within BaseHTTPRequestHandler.send_head 4887db96d56Sopenharmony_ci # so no errant scheme-less //netloc//evil.co/ domain mixup can happen. 4897db96d56Sopenharmony_ci attack_scheme_netloc_2slash_url = f'https://pypi.org/{url}' 4907db96d56Sopenharmony_ci expected_scheme_netloc_location = f'{attack_scheme_netloc_2slash_url}/' 4917db96d56Sopenharmony_ci response = self.request(attack_scheme_netloc_2slash_url) 4927db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) 4937db96d56Sopenharmony_ci location = response.getheader('Location') 4947db96d56Sopenharmony_ci # We're just ensuring that the scheme and domain make it through, if 4957db96d56Sopenharmony_ci # there are or aren't multiple slashes at the start of the path that 4967db96d56Sopenharmony_ci # follows that isn't important in this Location: header. 4977db96d56Sopenharmony_ci self.assertTrue(location.startswith('https://pypi.org/'), msg=location) 4987db96d56Sopenharmony_ci 4997db96d56Sopenharmony_ci def test_get(self): 5007db96d56Sopenharmony_ci #constructs the path relative to the root directory of the HTTPServer 5017db96d56Sopenharmony_ci response = self.request(self.base_url + '/test') 5027db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK, data=self.data) 5037db96d56Sopenharmony_ci # check for trailing "/" which should return 404. See Issue17324 5047db96d56Sopenharmony_ci response = self.request(self.base_url + '/test/') 5057db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.NOT_FOUND) 5067db96d56Sopenharmony_ci response = self.request(self.base_url + '/') 5077db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK) 5087db96d56Sopenharmony_ci response = self.request(self.base_url) 5097db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) 5107db96d56Sopenharmony_ci self.assertEqual(response.getheader("Content-Length"), "0") 5117db96d56Sopenharmony_ci response = self.request(self.base_url + '/?hi=2') 5127db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK) 5137db96d56Sopenharmony_ci response = self.request(self.base_url + '?hi=1') 5147db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) 5157db96d56Sopenharmony_ci self.assertEqual(response.getheader("Location"), 5167db96d56Sopenharmony_ci self.base_url + "/?hi=1") 5177db96d56Sopenharmony_ci response = self.request('/ThisDoesNotExist') 5187db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.NOT_FOUND) 5197db96d56Sopenharmony_ci response = self.request('/' + 'ThisDoesNotExist' + '/') 5207db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.NOT_FOUND) 5217db96d56Sopenharmony_ci os.makedirs(os.path.join(self.tempdir, 'spam', 'index.html')) 5227db96d56Sopenharmony_ci response = self.request(self.base_url + '/spam/') 5237db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK) 5247db96d56Sopenharmony_ci 5257db96d56Sopenharmony_ci data = b"Dummy index file\r\n" 5267db96d56Sopenharmony_ci with open(os.path.join(self.tempdir_name, 'index.html'), 'wb') as f: 5277db96d56Sopenharmony_ci f.write(data) 5287db96d56Sopenharmony_ci response = self.request(self.base_url + '/') 5297db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK, data) 5307db96d56Sopenharmony_ci 5317db96d56Sopenharmony_ci # chmod() doesn't work as expected on Windows, and filesystem 5327db96d56Sopenharmony_ci # permissions are ignored by root on Unix. 5337db96d56Sopenharmony_ci if os.name == 'posix' and os.geteuid() != 0: 5347db96d56Sopenharmony_ci os.chmod(self.tempdir, 0) 5357db96d56Sopenharmony_ci try: 5367db96d56Sopenharmony_ci response = self.request(self.base_url + '/') 5377db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.NOT_FOUND) 5387db96d56Sopenharmony_ci finally: 5397db96d56Sopenharmony_ci os.chmod(self.tempdir, 0o755) 5407db96d56Sopenharmony_ci 5417db96d56Sopenharmony_ci def test_head(self): 5427db96d56Sopenharmony_ci response = self.request( 5437db96d56Sopenharmony_ci self.base_url + '/test', method='HEAD') 5447db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK) 5457db96d56Sopenharmony_ci self.assertEqual(response.getheader('content-length'), 5467db96d56Sopenharmony_ci str(len(self.data))) 5477db96d56Sopenharmony_ci self.assertEqual(response.getheader('content-type'), 5487db96d56Sopenharmony_ci 'application/octet-stream') 5497db96d56Sopenharmony_ci 5507db96d56Sopenharmony_ci def test_browser_cache(self): 5517db96d56Sopenharmony_ci """Check that when a request to /test is sent with the request header 5527db96d56Sopenharmony_ci If-Modified-Since set to date of last modification, the server returns 5537db96d56Sopenharmony_ci status code 304, not 200 5547db96d56Sopenharmony_ci """ 5557db96d56Sopenharmony_ci headers = email.message.Message() 5567db96d56Sopenharmony_ci headers['If-Modified-Since'] = self.last_modif_header 5577db96d56Sopenharmony_ci response = self.request(self.base_url + '/test', headers=headers) 5587db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.NOT_MODIFIED) 5597db96d56Sopenharmony_ci 5607db96d56Sopenharmony_ci # one hour after last modification : must return 304 5617db96d56Sopenharmony_ci new_dt = self.last_modif_datetime + datetime.timedelta(hours=1) 5627db96d56Sopenharmony_ci headers = email.message.Message() 5637db96d56Sopenharmony_ci headers['If-Modified-Since'] = email.utils.format_datetime(new_dt, 5647db96d56Sopenharmony_ci usegmt=True) 5657db96d56Sopenharmony_ci response = self.request(self.base_url + '/test', headers=headers) 5667db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.NOT_MODIFIED) 5677db96d56Sopenharmony_ci 5687db96d56Sopenharmony_ci def test_browser_cache_file_changed(self): 5697db96d56Sopenharmony_ci # with If-Modified-Since earlier than Last-Modified, must return 200 5707db96d56Sopenharmony_ci dt = self.last_modif_datetime 5717db96d56Sopenharmony_ci # build datetime object : 365 days before last modification 5727db96d56Sopenharmony_ci old_dt = dt - datetime.timedelta(days=365) 5737db96d56Sopenharmony_ci headers = email.message.Message() 5747db96d56Sopenharmony_ci headers['If-Modified-Since'] = email.utils.format_datetime(old_dt, 5757db96d56Sopenharmony_ci usegmt=True) 5767db96d56Sopenharmony_ci response = self.request(self.base_url + '/test', headers=headers) 5777db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK) 5787db96d56Sopenharmony_ci 5797db96d56Sopenharmony_ci def test_browser_cache_with_If_None_Match_header(self): 5807db96d56Sopenharmony_ci # if If-None-Match header is present, ignore If-Modified-Since 5817db96d56Sopenharmony_ci 5827db96d56Sopenharmony_ci headers = email.message.Message() 5837db96d56Sopenharmony_ci headers['If-Modified-Since'] = self.last_modif_header 5847db96d56Sopenharmony_ci headers['If-None-Match'] = "*" 5857db96d56Sopenharmony_ci response = self.request(self.base_url + '/test', headers=headers) 5867db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK) 5877db96d56Sopenharmony_ci 5887db96d56Sopenharmony_ci def test_invalid_requests(self): 5897db96d56Sopenharmony_ci response = self.request('/', method='FOO') 5907db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED) 5917db96d56Sopenharmony_ci # requests must be case sensitive,so this should fail too 5927db96d56Sopenharmony_ci response = self.request('/', method='custom') 5937db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED) 5947db96d56Sopenharmony_ci response = self.request('/', method='GETs') 5957db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.NOT_IMPLEMENTED) 5967db96d56Sopenharmony_ci 5977db96d56Sopenharmony_ci def test_last_modified(self): 5987db96d56Sopenharmony_ci """Checks that the datetime returned in Last-Modified response header 5997db96d56Sopenharmony_ci is the actual datetime of last modification, rounded to the second 6007db96d56Sopenharmony_ci """ 6017db96d56Sopenharmony_ci response = self.request(self.base_url + '/test') 6027db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK, data=self.data) 6037db96d56Sopenharmony_ci last_modif_header = response.headers['Last-modified'] 6047db96d56Sopenharmony_ci self.assertEqual(last_modif_header, self.last_modif_header) 6057db96d56Sopenharmony_ci 6067db96d56Sopenharmony_ci def test_path_without_leading_slash(self): 6077db96d56Sopenharmony_ci response = self.request(self.tempdir_name + '/test') 6087db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK, data=self.data) 6097db96d56Sopenharmony_ci response = self.request(self.tempdir_name + '/test/') 6107db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.NOT_FOUND) 6117db96d56Sopenharmony_ci response = self.request(self.tempdir_name + '/') 6127db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK) 6137db96d56Sopenharmony_ci response = self.request(self.tempdir_name) 6147db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) 6157db96d56Sopenharmony_ci response = self.request(self.tempdir_name + '/?hi=2') 6167db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.OK) 6177db96d56Sopenharmony_ci response = self.request(self.tempdir_name + '?hi=1') 6187db96d56Sopenharmony_ci self.check_status_and_reason(response, HTTPStatus.MOVED_PERMANENTLY) 6197db96d56Sopenharmony_ci self.assertEqual(response.getheader("Location"), 6207db96d56Sopenharmony_ci self.tempdir_name + "/?hi=1") 6217db96d56Sopenharmony_ci 6227db96d56Sopenharmony_ci def test_html_escape_filename(self): 6237db96d56Sopenharmony_ci filename = '<test&>.txt' 6247db96d56Sopenharmony_ci fullpath = os.path.join(self.tempdir, filename) 6257db96d56Sopenharmony_ci 6267db96d56Sopenharmony_ci try: 6277db96d56Sopenharmony_ci open(fullpath, 'wb').close() 6287db96d56Sopenharmony_ci except OSError: 6297db96d56Sopenharmony_ci raise unittest.SkipTest('Can not create file %s on current file ' 6307db96d56Sopenharmony_ci 'system' % filename) 6317db96d56Sopenharmony_ci 6327db96d56Sopenharmony_ci try: 6337db96d56Sopenharmony_ci response = self.request(self.base_url + '/') 6347db96d56Sopenharmony_ci body = self.check_status_and_reason(response, HTTPStatus.OK) 6357db96d56Sopenharmony_ci enc = response.headers.get_content_charset() 6367db96d56Sopenharmony_ci finally: 6377db96d56Sopenharmony_ci os.unlink(fullpath) # avoid affecting test_undecodable_filename 6387db96d56Sopenharmony_ci 6397db96d56Sopenharmony_ci self.assertIsNotNone(enc) 6407db96d56Sopenharmony_ci html_text = '>%s<' % html.escape(filename, quote=False) 6417db96d56Sopenharmony_ci self.assertIn(html_text.encode(enc), body) 6427db96d56Sopenharmony_ci 6437db96d56Sopenharmony_ci 6447db96d56Sopenharmony_cicgi_file1 = """\ 6457db96d56Sopenharmony_ci#!%s 6467db96d56Sopenharmony_ci 6477db96d56Sopenharmony_ciprint("Content-type: text/html") 6487db96d56Sopenharmony_ciprint() 6497db96d56Sopenharmony_ciprint("Hello World") 6507db96d56Sopenharmony_ci""" 6517db96d56Sopenharmony_ci 6527db96d56Sopenharmony_cicgi_file2 = """\ 6537db96d56Sopenharmony_ci#!%s 6547db96d56Sopenharmony_ciimport os 6557db96d56Sopenharmony_ciimport sys 6567db96d56Sopenharmony_ciimport urllib.parse 6577db96d56Sopenharmony_ci 6587db96d56Sopenharmony_ciprint("Content-type: text/html") 6597db96d56Sopenharmony_ciprint() 6607db96d56Sopenharmony_ci 6617db96d56Sopenharmony_cicontent_length = int(os.environ["CONTENT_LENGTH"]) 6627db96d56Sopenharmony_ciquery_string = sys.stdin.buffer.read(content_length) 6637db96d56Sopenharmony_ciparams = {key.decode("utf-8"): val.decode("utf-8") 6647db96d56Sopenharmony_ci for key, val in urllib.parse.parse_qsl(query_string)} 6657db96d56Sopenharmony_ci 6667db96d56Sopenharmony_ciprint("%%s, %%s, %%s" %% (params["spam"], params["eggs"], params["bacon"])) 6677db96d56Sopenharmony_ci""" 6687db96d56Sopenharmony_ci 6697db96d56Sopenharmony_cicgi_file4 = """\ 6707db96d56Sopenharmony_ci#!%s 6717db96d56Sopenharmony_ciimport os 6727db96d56Sopenharmony_ci 6737db96d56Sopenharmony_ciprint("Content-type: text/html") 6747db96d56Sopenharmony_ciprint() 6757db96d56Sopenharmony_ci 6767db96d56Sopenharmony_ciprint(os.environ["%s"]) 6777db96d56Sopenharmony_ci""" 6787db96d56Sopenharmony_ci 6797db96d56Sopenharmony_cicgi_file6 = """\ 6807db96d56Sopenharmony_ci#!%s 6817db96d56Sopenharmony_ciimport os 6827db96d56Sopenharmony_ci 6837db96d56Sopenharmony_ciprint("X-ambv: was here") 6847db96d56Sopenharmony_ciprint("Content-type: text/html") 6857db96d56Sopenharmony_ciprint() 6867db96d56Sopenharmony_ciprint("<pre>") 6877db96d56Sopenharmony_cifor k, v in os.environ.items(): 6887db96d56Sopenharmony_ci try: 6897db96d56Sopenharmony_ci k.encode('ascii') 6907db96d56Sopenharmony_ci v.encode('ascii') 6917db96d56Sopenharmony_ci except UnicodeEncodeError: 6927db96d56Sopenharmony_ci continue # see: BPO-44647 6937db96d56Sopenharmony_ci print(f"{k}={v}") 6947db96d56Sopenharmony_ciprint("</pre>") 6957db96d56Sopenharmony_ci""" 6967db96d56Sopenharmony_ci 6977db96d56Sopenharmony_ci 6987db96d56Sopenharmony_ci@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, 6997db96d56Sopenharmony_ci "This test can't be run reliably as root (issue #13308).") 7007db96d56Sopenharmony_ciclass CGIHTTPServerTestCase(BaseTestCase): 7017db96d56Sopenharmony_ci class request_handler(NoLogRequestHandler, CGIHTTPRequestHandler): 7027db96d56Sopenharmony_ci pass 7037db96d56Sopenharmony_ci 7047db96d56Sopenharmony_ci linesep = os.linesep.encode('ascii') 7057db96d56Sopenharmony_ci 7067db96d56Sopenharmony_ci def setUp(self): 7077db96d56Sopenharmony_ci BaseTestCase.setUp(self) 7087db96d56Sopenharmony_ci self.cwd = os.getcwd() 7097db96d56Sopenharmony_ci self.parent_dir = tempfile.mkdtemp() 7107db96d56Sopenharmony_ci self.cgi_dir = os.path.join(self.parent_dir, 'cgi-bin') 7117db96d56Sopenharmony_ci self.cgi_child_dir = os.path.join(self.cgi_dir, 'child-dir') 7127db96d56Sopenharmony_ci self.sub_dir_1 = os.path.join(self.parent_dir, 'sub') 7137db96d56Sopenharmony_ci self.sub_dir_2 = os.path.join(self.sub_dir_1, 'dir') 7147db96d56Sopenharmony_ci self.cgi_dir_in_sub_dir = os.path.join(self.sub_dir_2, 'cgi-bin') 7157db96d56Sopenharmony_ci os.mkdir(self.cgi_dir) 7167db96d56Sopenharmony_ci os.mkdir(self.cgi_child_dir) 7177db96d56Sopenharmony_ci os.mkdir(self.sub_dir_1) 7187db96d56Sopenharmony_ci os.mkdir(self.sub_dir_2) 7197db96d56Sopenharmony_ci os.mkdir(self.cgi_dir_in_sub_dir) 7207db96d56Sopenharmony_ci self.nocgi_path = None 7217db96d56Sopenharmony_ci self.file1_path = None 7227db96d56Sopenharmony_ci self.file2_path = None 7237db96d56Sopenharmony_ci self.file3_path = None 7247db96d56Sopenharmony_ci self.file4_path = None 7257db96d56Sopenharmony_ci self.file5_path = None 7267db96d56Sopenharmony_ci 7277db96d56Sopenharmony_ci # The shebang line should be pure ASCII: use symlink if possible. 7287db96d56Sopenharmony_ci # See issue #7668. 7297db96d56Sopenharmony_ci self._pythonexe_symlink = None 7307db96d56Sopenharmony_ci if os_helper.can_symlink(): 7317db96d56Sopenharmony_ci self.pythonexe = os.path.join(self.parent_dir, 'python') 7327db96d56Sopenharmony_ci self._pythonexe_symlink = support.PythonSymlink(self.pythonexe).__enter__() 7337db96d56Sopenharmony_ci else: 7347db96d56Sopenharmony_ci self.pythonexe = sys.executable 7357db96d56Sopenharmony_ci 7367db96d56Sopenharmony_ci try: 7377db96d56Sopenharmony_ci # The python executable path is written as the first line of the 7387db96d56Sopenharmony_ci # CGI Python script. The encoding cookie cannot be used, and so the 7397db96d56Sopenharmony_ci # path should be encodable to the default script encoding (utf-8) 7407db96d56Sopenharmony_ci self.pythonexe.encode('utf-8') 7417db96d56Sopenharmony_ci except UnicodeEncodeError: 7427db96d56Sopenharmony_ci self.tearDown() 7437db96d56Sopenharmony_ci self.skipTest("Python executable path is not encodable to utf-8") 7447db96d56Sopenharmony_ci 7457db96d56Sopenharmony_ci self.nocgi_path = os.path.join(self.parent_dir, 'nocgi.py') 7467db96d56Sopenharmony_ci with open(self.nocgi_path, 'w', encoding='utf-8') as fp: 7477db96d56Sopenharmony_ci fp.write(cgi_file1 % self.pythonexe) 7487db96d56Sopenharmony_ci os.chmod(self.nocgi_path, 0o777) 7497db96d56Sopenharmony_ci 7507db96d56Sopenharmony_ci self.file1_path = os.path.join(self.cgi_dir, 'file1.py') 7517db96d56Sopenharmony_ci with open(self.file1_path, 'w', encoding='utf-8') as file1: 7527db96d56Sopenharmony_ci file1.write(cgi_file1 % self.pythonexe) 7537db96d56Sopenharmony_ci os.chmod(self.file1_path, 0o777) 7547db96d56Sopenharmony_ci 7557db96d56Sopenharmony_ci self.file2_path = os.path.join(self.cgi_dir, 'file2.py') 7567db96d56Sopenharmony_ci with open(self.file2_path, 'w', encoding='utf-8') as file2: 7577db96d56Sopenharmony_ci file2.write(cgi_file2 % self.pythonexe) 7587db96d56Sopenharmony_ci os.chmod(self.file2_path, 0o777) 7597db96d56Sopenharmony_ci 7607db96d56Sopenharmony_ci self.file3_path = os.path.join(self.cgi_child_dir, 'file3.py') 7617db96d56Sopenharmony_ci with open(self.file3_path, 'w', encoding='utf-8') as file3: 7627db96d56Sopenharmony_ci file3.write(cgi_file1 % self.pythonexe) 7637db96d56Sopenharmony_ci os.chmod(self.file3_path, 0o777) 7647db96d56Sopenharmony_ci 7657db96d56Sopenharmony_ci self.file4_path = os.path.join(self.cgi_dir, 'file4.py') 7667db96d56Sopenharmony_ci with open(self.file4_path, 'w', encoding='utf-8') as file4: 7677db96d56Sopenharmony_ci file4.write(cgi_file4 % (self.pythonexe, 'QUERY_STRING')) 7687db96d56Sopenharmony_ci os.chmod(self.file4_path, 0o777) 7697db96d56Sopenharmony_ci 7707db96d56Sopenharmony_ci self.file5_path = os.path.join(self.cgi_dir_in_sub_dir, 'file5.py') 7717db96d56Sopenharmony_ci with open(self.file5_path, 'w', encoding='utf-8') as file5: 7727db96d56Sopenharmony_ci file5.write(cgi_file1 % self.pythonexe) 7737db96d56Sopenharmony_ci os.chmod(self.file5_path, 0o777) 7747db96d56Sopenharmony_ci 7757db96d56Sopenharmony_ci self.file6_path = os.path.join(self.cgi_dir, 'file6.py') 7767db96d56Sopenharmony_ci with open(self.file6_path, 'w', encoding='utf-8') as file6: 7777db96d56Sopenharmony_ci file6.write(cgi_file6 % self.pythonexe) 7787db96d56Sopenharmony_ci os.chmod(self.file6_path, 0o777) 7797db96d56Sopenharmony_ci 7807db96d56Sopenharmony_ci os.chdir(self.parent_dir) 7817db96d56Sopenharmony_ci 7827db96d56Sopenharmony_ci def tearDown(self): 7837db96d56Sopenharmony_ci try: 7847db96d56Sopenharmony_ci os.chdir(self.cwd) 7857db96d56Sopenharmony_ci if self._pythonexe_symlink: 7867db96d56Sopenharmony_ci self._pythonexe_symlink.__exit__(None, None, None) 7877db96d56Sopenharmony_ci if self.nocgi_path: 7887db96d56Sopenharmony_ci os.remove(self.nocgi_path) 7897db96d56Sopenharmony_ci if self.file1_path: 7907db96d56Sopenharmony_ci os.remove(self.file1_path) 7917db96d56Sopenharmony_ci if self.file2_path: 7927db96d56Sopenharmony_ci os.remove(self.file2_path) 7937db96d56Sopenharmony_ci if self.file3_path: 7947db96d56Sopenharmony_ci os.remove(self.file3_path) 7957db96d56Sopenharmony_ci if self.file4_path: 7967db96d56Sopenharmony_ci os.remove(self.file4_path) 7977db96d56Sopenharmony_ci if self.file5_path: 7987db96d56Sopenharmony_ci os.remove(self.file5_path) 7997db96d56Sopenharmony_ci if self.file6_path: 8007db96d56Sopenharmony_ci os.remove(self.file6_path) 8017db96d56Sopenharmony_ci os.rmdir(self.cgi_child_dir) 8027db96d56Sopenharmony_ci os.rmdir(self.cgi_dir) 8037db96d56Sopenharmony_ci os.rmdir(self.cgi_dir_in_sub_dir) 8047db96d56Sopenharmony_ci os.rmdir(self.sub_dir_2) 8057db96d56Sopenharmony_ci os.rmdir(self.sub_dir_1) 8067db96d56Sopenharmony_ci os.rmdir(self.parent_dir) 8077db96d56Sopenharmony_ci finally: 8087db96d56Sopenharmony_ci BaseTestCase.tearDown(self) 8097db96d56Sopenharmony_ci 8107db96d56Sopenharmony_ci def test_url_collapse_path(self): 8117db96d56Sopenharmony_ci # verify tail is the last portion and head is the rest on proper urls 8127db96d56Sopenharmony_ci test_vectors = { 8137db96d56Sopenharmony_ci '': '//', 8147db96d56Sopenharmony_ci '..': IndexError, 8157db96d56Sopenharmony_ci '/.//..': IndexError, 8167db96d56Sopenharmony_ci '/': '//', 8177db96d56Sopenharmony_ci '//': '//', 8187db96d56Sopenharmony_ci '/\\': '//\\', 8197db96d56Sopenharmony_ci '/.//': '//', 8207db96d56Sopenharmony_ci 'cgi-bin/file1.py': '/cgi-bin/file1.py', 8217db96d56Sopenharmony_ci '/cgi-bin/file1.py': '/cgi-bin/file1.py', 8227db96d56Sopenharmony_ci 'a': '//a', 8237db96d56Sopenharmony_ci '/a': '//a', 8247db96d56Sopenharmony_ci '//a': '//a', 8257db96d56Sopenharmony_ci './a': '//a', 8267db96d56Sopenharmony_ci './C:/': '/C:/', 8277db96d56Sopenharmony_ci '/a/b': '/a/b', 8287db96d56Sopenharmony_ci '/a/b/': '/a/b/', 8297db96d56Sopenharmony_ci '/a/b/.': '/a/b/', 8307db96d56Sopenharmony_ci '/a/b/c/..': '/a/b/', 8317db96d56Sopenharmony_ci '/a/b/c/../d': '/a/b/d', 8327db96d56Sopenharmony_ci '/a/b/c/../d/e/../f': '/a/b/d/f', 8337db96d56Sopenharmony_ci '/a/b/c/../d/e/../../f': '/a/b/f', 8347db96d56Sopenharmony_ci '/a/b/c/../d/e/.././././..//f': '/a/b/f', 8357db96d56Sopenharmony_ci '../a/b/c/../d/e/.././././..//f': IndexError, 8367db96d56Sopenharmony_ci '/a/b/c/../d/e/../../../f': '/a/f', 8377db96d56Sopenharmony_ci '/a/b/c/../d/e/../../../../f': '//f', 8387db96d56Sopenharmony_ci '/a/b/c/../d/e/../../../../../f': IndexError, 8397db96d56Sopenharmony_ci '/a/b/c/../d/e/../../../../f/..': '//', 8407db96d56Sopenharmony_ci '/a/b/c/../d/e/../../../../f/../.': '//', 8417db96d56Sopenharmony_ci } 8427db96d56Sopenharmony_ci for path, expected in test_vectors.items(): 8437db96d56Sopenharmony_ci if isinstance(expected, type) and issubclass(expected, Exception): 8447db96d56Sopenharmony_ci self.assertRaises(expected, 8457db96d56Sopenharmony_ci server._url_collapse_path, path) 8467db96d56Sopenharmony_ci else: 8477db96d56Sopenharmony_ci actual = server._url_collapse_path(path) 8487db96d56Sopenharmony_ci self.assertEqual(expected, actual, 8497db96d56Sopenharmony_ci msg='path = %r\nGot: %r\nWanted: %r' % 8507db96d56Sopenharmony_ci (path, actual, expected)) 8517db96d56Sopenharmony_ci 8527db96d56Sopenharmony_ci def test_headers_and_content(self): 8537db96d56Sopenharmony_ci res = self.request('/cgi-bin/file1.py') 8547db96d56Sopenharmony_ci self.assertEqual( 8557db96d56Sopenharmony_ci (res.read(), res.getheader('Content-type'), res.status), 8567db96d56Sopenharmony_ci (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK)) 8577db96d56Sopenharmony_ci 8587db96d56Sopenharmony_ci def test_issue19435(self): 8597db96d56Sopenharmony_ci res = self.request('///////////nocgi.py/../cgi-bin/nothere.sh') 8607db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.NOT_FOUND) 8617db96d56Sopenharmony_ci 8627db96d56Sopenharmony_ci def test_post(self): 8637db96d56Sopenharmony_ci params = urllib.parse.urlencode( 8647db96d56Sopenharmony_ci {'spam' : 1, 'eggs' : 'python', 'bacon' : 123456}) 8657db96d56Sopenharmony_ci headers = {'Content-type' : 'application/x-www-form-urlencoded'} 8667db96d56Sopenharmony_ci res = self.request('/cgi-bin/file2.py', 'POST', params, headers) 8677db96d56Sopenharmony_ci 8687db96d56Sopenharmony_ci self.assertEqual(res.read(), b'1, python, 123456' + self.linesep) 8697db96d56Sopenharmony_ci 8707db96d56Sopenharmony_ci def test_invaliduri(self): 8717db96d56Sopenharmony_ci res = self.request('/cgi-bin/invalid') 8727db96d56Sopenharmony_ci res.read() 8737db96d56Sopenharmony_ci self.assertEqual(res.status, HTTPStatus.NOT_FOUND) 8747db96d56Sopenharmony_ci 8757db96d56Sopenharmony_ci def test_authorization(self): 8767db96d56Sopenharmony_ci headers = {b'Authorization' : b'Basic ' + 8777db96d56Sopenharmony_ci base64.b64encode(b'username:pass')} 8787db96d56Sopenharmony_ci res = self.request('/cgi-bin/file1.py', 'GET', headers=headers) 8797db96d56Sopenharmony_ci self.assertEqual( 8807db96d56Sopenharmony_ci (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), 8817db96d56Sopenharmony_ci (res.read(), res.getheader('Content-type'), res.status)) 8827db96d56Sopenharmony_ci 8837db96d56Sopenharmony_ci def test_no_leading_slash(self): 8847db96d56Sopenharmony_ci # http://bugs.python.org/issue2254 8857db96d56Sopenharmony_ci res = self.request('cgi-bin/file1.py') 8867db96d56Sopenharmony_ci self.assertEqual( 8877db96d56Sopenharmony_ci (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), 8887db96d56Sopenharmony_ci (res.read(), res.getheader('Content-type'), res.status)) 8897db96d56Sopenharmony_ci 8907db96d56Sopenharmony_ci def test_os_environ_is_not_altered(self): 8917db96d56Sopenharmony_ci signature = "Test CGI Server" 8927db96d56Sopenharmony_ci os.environ['SERVER_SOFTWARE'] = signature 8937db96d56Sopenharmony_ci res = self.request('/cgi-bin/file1.py') 8947db96d56Sopenharmony_ci self.assertEqual( 8957db96d56Sopenharmony_ci (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), 8967db96d56Sopenharmony_ci (res.read(), res.getheader('Content-type'), res.status)) 8977db96d56Sopenharmony_ci self.assertEqual(os.environ['SERVER_SOFTWARE'], signature) 8987db96d56Sopenharmony_ci 8997db96d56Sopenharmony_ci def test_urlquote_decoding_in_cgi_check(self): 9007db96d56Sopenharmony_ci res = self.request('/cgi-bin%2ffile1.py') 9017db96d56Sopenharmony_ci self.assertEqual( 9027db96d56Sopenharmony_ci (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), 9037db96d56Sopenharmony_ci (res.read(), res.getheader('Content-type'), res.status)) 9047db96d56Sopenharmony_ci 9057db96d56Sopenharmony_ci def test_nested_cgi_path_issue21323(self): 9067db96d56Sopenharmony_ci res = self.request('/cgi-bin/child-dir/file3.py') 9077db96d56Sopenharmony_ci self.assertEqual( 9087db96d56Sopenharmony_ci (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), 9097db96d56Sopenharmony_ci (res.read(), res.getheader('Content-type'), res.status)) 9107db96d56Sopenharmony_ci 9117db96d56Sopenharmony_ci def test_query_with_multiple_question_mark(self): 9127db96d56Sopenharmony_ci res = self.request('/cgi-bin/file4.py?a=b?c=d') 9137db96d56Sopenharmony_ci self.assertEqual( 9147db96d56Sopenharmony_ci (b'a=b?c=d' + self.linesep, 'text/html', HTTPStatus.OK), 9157db96d56Sopenharmony_ci (res.read(), res.getheader('Content-type'), res.status)) 9167db96d56Sopenharmony_ci 9177db96d56Sopenharmony_ci def test_query_with_continuous_slashes(self): 9187db96d56Sopenharmony_ci res = self.request('/cgi-bin/file4.py?k=aa%2F%2Fbb&//q//p//=//a//b//') 9197db96d56Sopenharmony_ci self.assertEqual( 9207db96d56Sopenharmony_ci (b'k=aa%2F%2Fbb&//q//p//=//a//b//' + self.linesep, 9217db96d56Sopenharmony_ci 'text/html', HTTPStatus.OK), 9227db96d56Sopenharmony_ci (res.read(), res.getheader('Content-type'), res.status)) 9237db96d56Sopenharmony_ci 9247db96d56Sopenharmony_ci def test_cgi_path_in_sub_directories(self): 9257db96d56Sopenharmony_ci try: 9267db96d56Sopenharmony_ci CGIHTTPRequestHandler.cgi_directories.append('/sub/dir/cgi-bin') 9277db96d56Sopenharmony_ci res = self.request('/sub/dir/cgi-bin/file5.py') 9287db96d56Sopenharmony_ci self.assertEqual( 9297db96d56Sopenharmony_ci (b'Hello World' + self.linesep, 'text/html', HTTPStatus.OK), 9307db96d56Sopenharmony_ci (res.read(), res.getheader('Content-type'), res.status)) 9317db96d56Sopenharmony_ci finally: 9327db96d56Sopenharmony_ci CGIHTTPRequestHandler.cgi_directories.remove('/sub/dir/cgi-bin') 9337db96d56Sopenharmony_ci 9347db96d56Sopenharmony_ci def test_accept(self): 9357db96d56Sopenharmony_ci browser_accept = \ 9367db96d56Sopenharmony_ci 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' 9377db96d56Sopenharmony_ci tests = ( 9387db96d56Sopenharmony_ci ((('Accept', browser_accept),), browser_accept), 9397db96d56Sopenharmony_ci ((), ''), 9407db96d56Sopenharmony_ci # Hack case to get two values for the one header 9417db96d56Sopenharmony_ci ((('Accept', 'text/html'), ('ACCEPT', 'text/plain')), 9427db96d56Sopenharmony_ci 'text/html,text/plain'), 9437db96d56Sopenharmony_ci ) 9447db96d56Sopenharmony_ci for headers, expected in tests: 9457db96d56Sopenharmony_ci headers = OrderedDict(headers) 9467db96d56Sopenharmony_ci with self.subTest(headers): 9477db96d56Sopenharmony_ci res = self.request('/cgi-bin/file6.py', 'GET', headers=headers) 9487db96d56Sopenharmony_ci self.assertEqual(http.HTTPStatus.OK, res.status) 9497db96d56Sopenharmony_ci expected = f"HTTP_ACCEPT={expected}".encode('ascii') 9507db96d56Sopenharmony_ci self.assertIn(expected, res.read()) 9517db96d56Sopenharmony_ci 9527db96d56Sopenharmony_ci 9537db96d56Sopenharmony_ciclass SocketlessRequestHandler(SimpleHTTPRequestHandler): 9547db96d56Sopenharmony_ci def __init__(self, directory=None): 9557db96d56Sopenharmony_ci request = mock.Mock() 9567db96d56Sopenharmony_ci request.makefile.return_value = BytesIO() 9577db96d56Sopenharmony_ci super().__init__(request, None, None, directory=directory) 9587db96d56Sopenharmony_ci 9597db96d56Sopenharmony_ci self.get_called = False 9607db96d56Sopenharmony_ci self.protocol_version = "HTTP/1.1" 9617db96d56Sopenharmony_ci 9627db96d56Sopenharmony_ci def do_GET(self): 9637db96d56Sopenharmony_ci self.get_called = True 9647db96d56Sopenharmony_ci self.send_response(HTTPStatus.OK) 9657db96d56Sopenharmony_ci self.send_header('Content-Type', 'text/html') 9667db96d56Sopenharmony_ci self.end_headers() 9677db96d56Sopenharmony_ci self.wfile.write(b'<html><body>Data</body></html>\r\n') 9687db96d56Sopenharmony_ci 9697db96d56Sopenharmony_ci def log_message(self, format, *args): 9707db96d56Sopenharmony_ci pass 9717db96d56Sopenharmony_ci 9727db96d56Sopenharmony_ciclass RejectingSocketlessRequestHandler(SocketlessRequestHandler): 9737db96d56Sopenharmony_ci def handle_expect_100(self): 9747db96d56Sopenharmony_ci self.send_error(HTTPStatus.EXPECTATION_FAILED) 9757db96d56Sopenharmony_ci return False 9767db96d56Sopenharmony_ci 9777db96d56Sopenharmony_ci 9787db96d56Sopenharmony_ciclass AuditableBytesIO: 9797db96d56Sopenharmony_ci 9807db96d56Sopenharmony_ci def __init__(self): 9817db96d56Sopenharmony_ci self.datas = [] 9827db96d56Sopenharmony_ci 9837db96d56Sopenharmony_ci def write(self, data): 9847db96d56Sopenharmony_ci self.datas.append(data) 9857db96d56Sopenharmony_ci 9867db96d56Sopenharmony_ci def getData(self): 9877db96d56Sopenharmony_ci return b''.join(self.datas) 9887db96d56Sopenharmony_ci 9897db96d56Sopenharmony_ci @property 9907db96d56Sopenharmony_ci def numWrites(self): 9917db96d56Sopenharmony_ci return len(self.datas) 9927db96d56Sopenharmony_ci 9937db96d56Sopenharmony_ci 9947db96d56Sopenharmony_ciclass BaseHTTPRequestHandlerTestCase(unittest.TestCase): 9957db96d56Sopenharmony_ci """Test the functionality of the BaseHTTPServer. 9967db96d56Sopenharmony_ci 9977db96d56Sopenharmony_ci Test the support for the Expect 100-continue header. 9987db96d56Sopenharmony_ci """ 9997db96d56Sopenharmony_ci 10007db96d56Sopenharmony_ci HTTPResponseMatch = re.compile(b'HTTP/1.[0-9]+ 200 OK') 10017db96d56Sopenharmony_ci 10027db96d56Sopenharmony_ci def setUp (self): 10037db96d56Sopenharmony_ci self.handler = SocketlessRequestHandler() 10047db96d56Sopenharmony_ci 10057db96d56Sopenharmony_ci def send_typical_request(self, message): 10067db96d56Sopenharmony_ci input = BytesIO(message) 10077db96d56Sopenharmony_ci output = BytesIO() 10087db96d56Sopenharmony_ci self.handler.rfile = input 10097db96d56Sopenharmony_ci self.handler.wfile = output 10107db96d56Sopenharmony_ci self.handler.handle_one_request() 10117db96d56Sopenharmony_ci output.seek(0) 10127db96d56Sopenharmony_ci return output.readlines() 10137db96d56Sopenharmony_ci 10147db96d56Sopenharmony_ci def verify_get_called(self): 10157db96d56Sopenharmony_ci self.assertTrue(self.handler.get_called) 10167db96d56Sopenharmony_ci 10177db96d56Sopenharmony_ci def verify_expected_headers(self, headers): 10187db96d56Sopenharmony_ci for fieldName in b'Server: ', b'Date: ', b'Content-Type: ': 10197db96d56Sopenharmony_ci self.assertEqual(sum(h.startswith(fieldName) for h in headers), 1) 10207db96d56Sopenharmony_ci 10217db96d56Sopenharmony_ci def verify_http_server_response(self, response): 10227db96d56Sopenharmony_ci match = self.HTTPResponseMatch.search(response) 10237db96d56Sopenharmony_ci self.assertIsNotNone(match) 10247db96d56Sopenharmony_ci 10257db96d56Sopenharmony_ci def test_unprintable_not_logged(self): 10267db96d56Sopenharmony_ci # We call the method from the class directly as our Socketless 10277db96d56Sopenharmony_ci # Handler subclass overrode it... nice for everything BUT this test. 10287db96d56Sopenharmony_ci self.handler.client_address = ('127.0.0.1', 1337) 10297db96d56Sopenharmony_ci log_message = BaseHTTPRequestHandler.log_message 10307db96d56Sopenharmony_ci with mock.patch.object(sys, 'stderr', StringIO()) as fake_stderr: 10317db96d56Sopenharmony_ci log_message(self.handler, '/foo') 10327db96d56Sopenharmony_ci log_message(self.handler, '/\033bar\000\033') 10337db96d56Sopenharmony_ci log_message(self.handler, '/spam %s.', 'a') 10347db96d56Sopenharmony_ci log_message(self.handler, '/spam %s.', '\033\x7f\x9f\xa0beans') 10357db96d56Sopenharmony_ci log_message(self.handler, '"GET /foo\\b"ar\007 HTTP/1.0"') 10367db96d56Sopenharmony_ci stderr = fake_stderr.getvalue() 10377db96d56Sopenharmony_ci self.assertNotIn('\033', stderr) # non-printable chars are caught. 10387db96d56Sopenharmony_ci self.assertNotIn('\000', stderr) # non-printable chars are caught. 10397db96d56Sopenharmony_ci lines = stderr.splitlines() 10407db96d56Sopenharmony_ci self.assertIn('/foo', lines[0]) 10417db96d56Sopenharmony_ci self.assertIn(r'/\x1bbar\x00\x1b', lines[1]) 10427db96d56Sopenharmony_ci self.assertIn('/spam a.', lines[2]) 10437db96d56Sopenharmony_ci self.assertIn('/spam \\x1b\\x7f\\x9f\xa0beans.', lines[3]) 10447db96d56Sopenharmony_ci self.assertIn(r'"GET /foo\\b"ar\x07 HTTP/1.0"', lines[4]) 10457db96d56Sopenharmony_ci 10467db96d56Sopenharmony_ci def test_http_1_1(self): 10477db96d56Sopenharmony_ci result = self.send_typical_request(b'GET / HTTP/1.1\r\n\r\n') 10487db96d56Sopenharmony_ci self.verify_http_server_response(result[0]) 10497db96d56Sopenharmony_ci self.verify_expected_headers(result[1:-1]) 10507db96d56Sopenharmony_ci self.verify_get_called() 10517db96d56Sopenharmony_ci self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n') 10527db96d56Sopenharmony_ci self.assertEqual(self.handler.requestline, 'GET / HTTP/1.1') 10537db96d56Sopenharmony_ci self.assertEqual(self.handler.command, 'GET') 10547db96d56Sopenharmony_ci self.assertEqual(self.handler.path, '/') 10557db96d56Sopenharmony_ci self.assertEqual(self.handler.request_version, 'HTTP/1.1') 10567db96d56Sopenharmony_ci self.assertSequenceEqual(self.handler.headers.items(), ()) 10577db96d56Sopenharmony_ci 10587db96d56Sopenharmony_ci def test_http_1_0(self): 10597db96d56Sopenharmony_ci result = self.send_typical_request(b'GET / HTTP/1.0\r\n\r\n') 10607db96d56Sopenharmony_ci self.verify_http_server_response(result[0]) 10617db96d56Sopenharmony_ci self.verify_expected_headers(result[1:-1]) 10627db96d56Sopenharmony_ci self.verify_get_called() 10637db96d56Sopenharmony_ci self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n') 10647db96d56Sopenharmony_ci self.assertEqual(self.handler.requestline, 'GET / HTTP/1.0') 10657db96d56Sopenharmony_ci self.assertEqual(self.handler.command, 'GET') 10667db96d56Sopenharmony_ci self.assertEqual(self.handler.path, '/') 10677db96d56Sopenharmony_ci self.assertEqual(self.handler.request_version, 'HTTP/1.0') 10687db96d56Sopenharmony_ci self.assertSequenceEqual(self.handler.headers.items(), ()) 10697db96d56Sopenharmony_ci 10707db96d56Sopenharmony_ci def test_http_0_9(self): 10717db96d56Sopenharmony_ci result = self.send_typical_request(b'GET / HTTP/0.9\r\n\r\n') 10727db96d56Sopenharmony_ci self.assertEqual(len(result), 1) 10737db96d56Sopenharmony_ci self.assertEqual(result[0], b'<html><body>Data</body></html>\r\n') 10747db96d56Sopenharmony_ci self.verify_get_called() 10757db96d56Sopenharmony_ci 10767db96d56Sopenharmony_ci def test_extra_space(self): 10777db96d56Sopenharmony_ci result = self.send_typical_request( 10787db96d56Sopenharmony_ci b'GET /spaced out HTTP/1.1\r\n' 10797db96d56Sopenharmony_ci b'Host: dummy\r\n' 10807db96d56Sopenharmony_ci b'\r\n' 10817db96d56Sopenharmony_ci ) 10827db96d56Sopenharmony_ci self.assertTrue(result[0].startswith(b'HTTP/1.1 400 ')) 10837db96d56Sopenharmony_ci self.verify_expected_headers(result[1:result.index(b'\r\n')]) 10847db96d56Sopenharmony_ci self.assertFalse(self.handler.get_called) 10857db96d56Sopenharmony_ci 10867db96d56Sopenharmony_ci def test_with_continue_1_0(self): 10877db96d56Sopenharmony_ci result = self.send_typical_request(b'GET / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n') 10887db96d56Sopenharmony_ci self.verify_http_server_response(result[0]) 10897db96d56Sopenharmony_ci self.verify_expected_headers(result[1:-1]) 10907db96d56Sopenharmony_ci self.verify_get_called() 10917db96d56Sopenharmony_ci self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n') 10927db96d56Sopenharmony_ci self.assertEqual(self.handler.requestline, 'GET / HTTP/1.0') 10937db96d56Sopenharmony_ci self.assertEqual(self.handler.command, 'GET') 10947db96d56Sopenharmony_ci self.assertEqual(self.handler.path, '/') 10957db96d56Sopenharmony_ci self.assertEqual(self.handler.request_version, 'HTTP/1.0') 10967db96d56Sopenharmony_ci headers = (("Expect", "100-continue"),) 10977db96d56Sopenharmony_ci self.assertSequenceEqual(self.handler.headers.items(), headers) 10987db96d56Sopenharmony_ci 10997db96d56Sopenharmony_ci def test_with_continue_1_1(self): 11007db96d56Sopenharmony_ci result = self.send_typical_request(b'GET / HTTP/1.1\r\nExpect: 100-continue\r\n\r\n') 11017db96d56Sopenharmony_ci self.assertEqual(result[0], b'HTTP/1.1 100 Continue\r\n') 11027db96d56Sopenharmony_ci self.assertEqual(result[1], b'\r\n') 11037db96d56Sopenharmony_ci self.assertEqual(result[2], b'HTTP/1.1 200 OK\r\n') 11047db96d56Sopenharmony_ci self.verify_expected_headers(result[2:-1]) 11057db96d56Sopenharmony_ci self.verify_get_called() 11067db96d56Sopenharmony_ci self.assertEqual(result[-1], b'<html><body>Data</body></html>\r\n') 11077db96d56Sopenharmony_ci self.assertEqual(self.handler.requestline, 'GET / HTTP/1.1') 11087db96d56Sopenharmony_ci self.assertEqual(self.handler.command, 'GET') 11097db96d56Sopenharmony_ci self.assertEqual(self.handler.path, '/') 11107db96d56Sopenharmony_ci self.assertEqual(self.handler.request_version, 'HTTP/1.1') 11117db96d56Sopenharmony_ci headers = (("Expect", "100-continue"),) 11127db96d56Sopenharmony_ci self.assertSequenceEqual(self.handler.headers.items(), headers) 11137db96d56Sopenharmony_ci 11147db96d56Sopenharmony_ci def test_header_buffering_of_send_error(self): 11157db96d56Sopenharmony_ci 11167db96d56Sopenharmony_ci input = BytesIO(b'GET / HTTP/1.1\r\n\r\n') 11177db96d56Sopenharmony_ci output = AuditableBytesIO() 11187db96d56Sopenharmony_ci handler = SocketlessRequestHandler() 11197db96d56Sopenharmony_ci handler.rfile = input 11207db96d56Sopenharmony_ci handler.wfile = output 11217db96d56Sopenharmony_ci handler.request_version = 'HTTP/1.1' 11227db96d56Sopenharmony_ci handler.requestline = '' 11237db96d56Sopenharmony_ci handler.command = None 11247db96d56Sopenharmony_ci 11257db96d56Sopenharmony_ci handler.send_error(418) 11267db96d56Sopenharmony_ci self.assertEqual(output.numWrites, 2) 11277db96d56Sopenharmony_ci 11287db96d56Sopenharmony_ci def test_header_buffering_of_send_response_only(self): 11297db96d56Sopenharmony_ci 11307db96d56Sopenharmony_ci input = BytesIO(b'GET / HTTP/1.1\r\n\r\n') 11317db96d56Sopenharmony_ci output = AuditableBytesIO() 11327db96d56Sopenharmony_ci handler = SocketlessRequestHandler() 11337db96d56Sopenharmony_ci handler.rfile = input 11347db96d56Sopenharmony_ci handler.wfile = output 11357db96d56Sopenharmony_ci handler.request_version = 'HTTP/1.1' 11367db96d56Sopenharmony_ci 11377db96d56Sopenharmony_ci handler.send_response_only(418) 11387db96d56Sopenharmony_ci self.assertEqual(output.numWrites, 0) 11397db96d56Sopenharmony_ci handler.end_headers() 11407db96d56Sopenharmony_ci self.assertEqual(output.numWrites, 1) 11417db96d56Sopenharmony_ci 11427db96d56Sopenharmony_ci def test_header_buffering_of_send_header(self): 11437db96d56Sopenharmony_ci 11447db96d56Sopenharmony_ci input = BytesIO(b'GET / HTTP/1.1\r\n\r\n') 11457db96d56Sopenharmony_ci output = AuditableBytesIO() 11467db96d56Sopenharmony_ci handler = SocketlessRequestHandler() 11477db96d56Sopenharmony_ci handler.rfile = input 11487db96d56Sopenharmony_ci handler.wfile = output 11497db96d56Sopenharmony_ci handler.request_version = 'HTTP/1.1' 11507db96d56Sopenharmony_ci 11517db96d56Sopenharmony_ci handler.send_header('Foo', 'foo') 11527db96d56Sopenharmony_ci handler.send_header('bar', 'bar') 11537db96d56Sopenharmony_ci self.assertEqual(output.numWrites, 0) 11547db96d56Sopenharmony_ci handler.end_headers() 11557db96d56Sopenharmony_ci self.assertEqual(output.getData(), b'Foo: foo\r\nbar: bar\r\n\r\n') 11567db96d56Sopenharmony_ci self.assertEqual(output.numWrites, 1) 11577db96d56Sopenharmony_ci 11587db96d56Sopenharmony_ci def test_header_unbuffered_when_continue(self): 11597db96d56Sopenharmony_ci 11607db96d56Sopenharmony_ci def _readAndReseek(f): 11617db96d56Sopenharmony_ci pos = f.tell() 11627db96d56Sopenharmony_ci f.seek(0) 11637db96d56Sopenharmony_ci data = f.read() 11647db96d56Sopenharmony_ci f.seek(pos) 11657db96d56Sopenharmony_ci return data 11667db96d56Sopenharmony_ci 11677db96d56Sopenharmony_ci input = BytesIO(b'GET / HTTP/1.1\r\nExpect: 100-continue\r\n\r\n') 11687db96d56Sopenharmony_ci output = BytesIO() 11697db96d56Sopenharmony_ci self.handler.rfile = input 11707db96d56Sopenharmony_ci self.handler.wfile = output 11717db96d56Sopenharmony_ci self.handler.request_version = 'HTTP/1.1' 11727db96d56Sopenharmony_ci 11737db96d56Sopenharmony_ci self.handler.handle_one_request() 11747db96d56Sopenharmony_ci self.assertNotEqual(_readAndReseek(output), b'') 11757db96d56Sopenharmony_ci result = _readAndReseek(output).split(b'\r\n') 11767db96d56Sopenharmony_ci self.assertEqual(result[0], b'HTTP/1.1 100 Continue') 11777db96d56Sopenharmony_ci self.assertEqual(result[1], b'') 11787db96d56Sopenharmony_ci self.assertEqual(result[2], b'HTTP/1.1 200 OK') 11797db96d56Sopenharmony_ci 11807db96d56Sopenharmony_ci def test_with_continue_rejected(self): 11817db96d56Sopenharmony_ci usual_handler = self.handler # Save to avoid breaking any subsequent tests. 11827db96d56Sopenharmony_ci self.handler = RejectingSocketlessRequestHandler() 11837db96d56Sopenharmony_ci result = self.send_typical_request(b'GET / HTTP/1.1\r\nExpect: 100-continue\r\n\r\n') 11847db96d56Sopenharmony_ci self.assertEqual(result[0], b'HTTP/1.1 417 Expectation Failed\r\n') 11857db96d56Sopenharmony_ci self.verify_expected_headers(result[1:-1]) 11867db96d56Sopenharmony_ci # The expect handler should short circuit the usual get method by 11877db96d56Sopenharmony_ci # returning false here, so get_called should be false 11887db96d56Sopenharmony_ci self.assertFalse(self.handler.get_called) 11897db96d56Sopenharmony_ci self.assertEqual(sum(r == b'Connection: close\r\n' for r in result[1:-1]), 1) 11907db96d56Sopenharmony_ci self.handler = usual_handler # Restore to avoid breaking any subsequent tests. 11917db96d56Sopenharmony_ci 11927db96d56Sopenharmony_ci def test_request_length(self): 11937db96d56Sopenharmony_ci # Issue #10714: huge request lines are discarded, to avoid Denial 11947db96d56Sopenharmony_ci # of Service attacks. 11957db96d56Sopenharmony_ci result = self.send_typical_request(b'GET ' + b'x' * 65537) 11967db96d56Sopenharmony_ci self.assertEqual(result[0], b'HTTP/1.1 414 Request-URI Too Long\r\n') 11977db96d56Sopenharmony_ci self.assertFalse(self.handler.get_called) 11987db96d56Sopenharmony_ci self.assertIsInstance(self.handler.requestline, str) 11997db96d56Sopenharmony_ci 12007db96d56Sopenharmony_ci def test_header_length(self): 12017db96d56Sopenharmony_ci # Issue #6791: same for headers 12027db96d56Sopenharmony_ci result = self.send_typical_request( 12037db96d56Sopenharmony_ci b'GET / HTTP/1.1\r\nX-Foo: bar' + b'r' * 65537 + b'\r\n\r\n') 12047db96d56Sopenharmony_ci self.assertEqual(result[0], b'HTTP/1.1 431 Line too long\r\n') 12057db96d56Sopenharmony_ci self.assertFalse(self.handler.get_called) 12067db96d56Sopenharmony_ci self.assertEqual(self.handler.requestline, 'GET / HTTP/1.1') 12077db96d56Sopenharmony_ci 12087db96d56Sopenharmony_ci def test_too_many_headers(self): 12097db96d56Sopenharmony_ci result = self.send_typical_request( 12107db96d56Sopenharmony_ci b'GET / HTTP/1.1\r\n' + b'X-Foo: bar\r\n' * 101 + b'\r\n') 12117db96d56Sopenharmony_ci self.assertEqual(result[0], b'HTTP/1.1 431 Too many headers\r\n') 12127db96d56Sopenharmony_ci self.assertFalse(self.handler.get_called) 12137db96d56Sopenharmony_ci self.assertEqual(self.handler.requestline, 'GET / HTTP/1.1') 12147db96d56Sopenharmony_ci 12157db96d56Sopenharmony_ci def test_html_escape_on_error(self): 12167db96d56Sopenharmony_ci result = self.send_typical_request( 12177db96d56Sopenharmony_ci b'<script>alert("hello")</script> / HTTP/1.1') 12187db96d56Sopenharmony_ci result = b''.join(result) 12197db96d56Sopenharmony_ci text = '<script>alert("hello")</script>' 12207db96d56Sopenharmony_ci self.assertIn(html.escape(text, quote=False).encode('ascii'), result) 12217db96d56Sopenharmony_ci 12227db96d56Sopenharmony_ci def test_close_connection(self): 12237db96d56Sopenharmony_ci # handle_one_request() should be repeatedly called until 12247db96d56Sopenharmony_ci # it sets close_connection 12257db96d56Sopenharmony_ci def handle_one_request(): 12267db96d56Sopenharmony_ci self.handler.close_connection = next(close_values) 12277db96d56Sopenharmony_ci self.handler.handle_one_request = handle_one_request 12287db96d56Sopenharmony_ci 12297db96d56Sopenharmony_ci close_values = iter((True,)) 12307db96d56Sopenharmony_ci self.handler.handle() 12317db96d56Sopenharmony_ci self.assertRaises(StopIteration, next, close_values) 12327db96d56Sopenharmony_ci 12337db96d56Sopenharmony_ci close_values = iter((False, False, True)) 12347db96d56Sopenharmony_ci self.handler.handle() 12357db96d56Sopenharmony_ci self.assertRaises(StopIteration, next, close_values) 12367db96d56Sopenharmony_ci 12377db96d56Sopenharmony_ci def test_date_time_string(self): 12387db96d56Sopenharmony_ci now = time.time() 12397db96d56Sopenharmony_ci # this is the old code that formats the timestamp 12407db96d56Sopenharmony_ci year, month, day, hh, mm, ss, wd, y, z = time.gmtime(now) 12417db96d56Sopenharmony_ci expected = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( 12427db96d56Sopenharmony_ci self.handler.weekdayname[wd], 12437db96d56Sopenharmony_ci day, 12447db96d56Sopenharmony_ci self.handler.monthname[month], 12457db96d56Sopenharmony_ci year, hh, mm, ss 12467db96d56Sopenharmony_ci ) 12477db96d56Sopenharmony_ci self.assertEqual(self.handler.date_time_string(timestamp=now), expected) 12487db96d56Sopenharmony_ci 12497db96d56Sopenharmony_ci 12507db96d56Sopenharmony_ciclass SimpleHTTPRequestHandlerTestCase(unittest.TestCase): 12517db96d56Sopenharmony_ci """ Test url parsing """ 12527db96d56Sopenharmony_ci def setUp(self): 12537db96d56Sopenharmony_ci self.translated_1 = os.path.join(os.getcwd(), 'filename') 12547db96d56Sopenharmony_ci self.translated_2 = os.path.join('foo', 'filename') 12557db96d56Sopenharmony_ci self.translated_3 = os.path.join('bar', 'filename') 12567db96d56Sopenharmony_ci self.handler_1 = SocketlessRequestHandler() 12577db96d56Sopenharmony_ci self.handler_2 = SocketlessRequestHandler(directory='foo') 12587db96d56Sopenharmony_ci self.handler_3 = SocketlessRequestHandler(directory=pathlib.PurePath('bar')) 12597db96d56Sopenharmony_ci 12607db96d56Sopenharmony_ci def test_query_arguments(self): 12617db96d56Sopenharmony_ci path = self.handler_1.translate_path('/filename') 12627db96d56Sopenharmony_ci self.assertEqual(path, self.translated_1) 12637db96d56Sopenharmony_ci path = self.handler_2.translate_path('/filename') 12647db96d56Sopenharmony_ci self.assertEqual(path, self.translated_2) 12657db96d56Sopenharmony_ci path = self.handler_3.translate_path('/filename') 12667db96d56Sopenharmony_ci self.assertEqual(path, self.translated_3) 12677db96d56Sopenharmony_ci 12687db96d56Sopenharmony_ci path = self.handler_1.translate_path('/filename?foo=bar') 12697db96d56Sopenharmony_ci self.assertEqual(path, self.translated_1) 12707db96d56Sopenharmony_ci path = self.handler_2.translate_path('/filename?foo=bar') 12717db96d56Sopenharmony_ci self.assertEqual(path, self.translated_2) 12727db96d56Sopenharmony_ci path = self.handler_3.translate_path('/filename?foo=bar') 12737db96d56Sopenharmony_ci self.assertEqual(path, self.translated_3) 12747db96d56Sopenharmony_ci 12757db96d56Sopenharmony_ci path = self.handler_1.translate_path('/filename?a=b&spam=eggs#zot') 12767db96d56Sopenharmony_ci self.assertEqual(path, self.translated_1) 12777db96d56Sopenharmony_ci path = self.handler_2.translate_path('/filename?a=b&spam=eggs#zot') 12787db96d56Sopenharmony_ci self.assertEqual(path, self.translated_2) 12797db96d56Sopenharmony_ci path = self.handler_3.translate_path('/filename?a=b&spam=eggs#zot') 12807db96d56Sopenharmony_ci self.assertEqual(path, self.translated_3) 12817db96d56Sopenharmony_ci 12827db96d56Sopenharmony_ci def test_start_with_double_slash(self): 12837db96d56Sopenharmony_ci path = self.handler_1.translate_path('//filename') 12847db96d56Sopenharmony_ci self.assertEqual(path, self.translated_1) 12857db96d56Sopenharmony_ci path = self.handler_2.translate_path('//filename') 12867db96d56Sopenharmony_ci self.assertEqual(path, self.translated_2) 12877db96d56Sopenharmony_ci path = self.handler_3.translate_path('//filename') 12887db96d56Sopenharmony_ci self.assertEqual(path, self.translated_3) 12897db96d56Sopenharmony_ci 12907db96d56Sopenharmony_ci path = self.handler_1.translate_path('//filename?foo=bar') 12917db96d56Sopenharmony_ci self.assertEqual(path, self.translated_1) 12927db96d56Sopenharmony_ci path = self.handler_2.translate_path('//filename?foo=bar') 12937db96d56Sopenharmony_ci self.assertEqual(path, self.translated_2) 12947db96d56Sopenharmony_ci path = self.handler_3.translate_path('//filename?foo=bar') 12957db96d56Sopenharmony_ci self.assertEqual(path, self.translated_3) 12967db96d56Sopenharmony_ci 12977db96d56Sopenharmony_ci def test_windows_colon(self): 12987db96d56Sopenharmony_ci with support.swap_attr(server.os, 'path', ntpath): 12997db96d56Sopenharmony_ci path = self.handler_1.translate_path('c:c:c:foo/filename') 13007db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13017db96d56Sopenharmony_ci self.assertEqual(path, self.translated_1) 13027db96d56Sopenharmony_ci path = self.handler_2.translate_path('c:c:c:foo/filename') 13037db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13047db96d56Sopenharmony_ci self.assertEqual(path, self.translated_2) 13057db96d56Sopenharmony_ci path = self.handler_3.translate_path('c:c:c:foo/filename') 13067db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13077db96d56Sopenharmony_ci self.assertEqual(path, self.translated_3) 13087db96d56Sopenharmony_ci 13097db96d56Sopenharmony_ci path = self.handler_1.translate_path('\\c:../filename') 13107db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13117db96d56Sopenharmony_ci self.assertEqual(path, self.translated_1) 13127db96d56Sopenharmony_ci path = self.handler_2.translate_path('\\c:../filename') 13137db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13147db96d56Sopenharmony_ci self.assertEqual(path, self.translated_2) 13157db96d56Sopenharmony_ci path = self.handler_3.translate_path('\\c:../filename') 13167db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13177db96d56Sopenharmony_ci self.assertEqual(path, self.translated_3) 13187db96d56Sopenharmony_ci 13197db96d56Sopenharmony_ci path = self.handler_1.translate_path('c:\\c:..\\foo/filename') 13207db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13217db96d56Sopenharmony_ci self.assertEqual(path, self.translated_1) 13227db96d56Sopenharmony_ci path = self.handler_2.translate_path('c:\\c:..\\foo/filename') 13237db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13247db96d56Sopenharmony_ci self.assertEqual(path, self.translated_2) 13257db96d56Sopenharmony_ci path = self.handler_3.translate_path('c:\\c:..\\foo/filename') 13267db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13277db96d56Sopenharmony_ci self.assertEqual(path, self.translated_3) 13287db96d56Sopenharmony_ci 13297db96d56Sopenharmony_ci path = self.handler_1.translate_path('c:c:foo\\c:c:bar/filename') 13307db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13317db96d56Sopenharmony_ci self.assertEqual(path, self.translated_1) 13327db96d56Sopenharmony_ci path = self.handler_2.translate_path('c:c:foo\\c:c:bar/filename') 13337db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13347db96d56Sopenharmony_ci self.assertEqual(path, self.translated_2) 13357db96d56Sopenharmony_ci path = self.handler_3.translate_path('c:c:foo\\c:c:bar/filename') 13367db96d56Sopenharmony_ci path = path.replace(ntpath.sep, os.sep) 13377db96d56Sopenharmony_ci self.assertEqual(path, self.translated_3) 13387db96d56Sopenharmony_ci 13397db96d56Sopenharmony_ci 13407db96d56Sopenharmony_ciclass MiscTestCase(unittest.TestCase): 13417db96d56Sopenharmony_ci def test_all(self): 13427db96d56Sopenharmony_ci expected = [] 13437db96d56Sopenharmony_ci denylist = {'executable', 'nobody_uid', 'test'} 13447db96d56Sopenharmony_ci for name in dir(server): 13457db96d56Sopenharmony_ci if name.startswith('_') or name in denylist: 13467db96d56Sopenharmony_ci continue 13477db96d56Sopenharmony_ci module_object = getattr(server, name) 13487db96d56Sopenharmony_ci if getattr(module_object, '__module__', None) == 'http.server': 13497db96d56Sopenharmony_ci expected.append(name) 13507db96d56Sopenharmony_ci self.assertCountEqual(server.__all__, expected) 13517db96d56Sopenharmony_ci 13527db96d56Sopenharmony_ci 13537db96d56Sopenharmony_ciclass ScriptTestCase(unittest.TestCase): 13547db96d56Sopenharmony_ci 13557db96d56Sopenharmony_ci def mock_server_class(self): 13567db96d56Sopenharmony_ci return mock.MagicMock( 13577db96d56Sopenharmony_ci return_value=mock.MagicMock( 13587db96d56Sopenharmony_ci __enter__=mock.MagicMock( 13597db96d56Sopenharmony_ci return_value=mock.MagicMock( 13607db96d56Sopenharmony_ci socket=mock.MagicMock( 13617db96d56Sopenharmony_ci getsockname=lambda: ('', 0), 13627db96d56Sopenharmony_ci ), 13637db96d56Sopenharmony_ci ), 13647db96d56Sopenharmony_ci ), 13657db96d56Sopenharmony_ci ), 13667db96d56Sopenharmony_ci ) 13677db96d56Sopenharmony_ci 13687db96d56Sopenharmony_ci @mock.patch('builtins.print') 13697db96d56Sopenharmony_ci def test_server_test_unspec(self, _): 13707db96d56Sopenharmony_ci mock_server = self.mock_server_class() 13717db96d56Sopenharmony_ci server.test(ServerClass=mock_server, bind=None) 13727db96d56Sopenharmony_ci self.assertIn( 13737db96d56Sopenharmony_ci mock_server.address_family, 13747db96d56Sopenharmony_ci (socket.AF_INET6, socket.AF_INET), 13757db96d56Sopenharmony_ci ) 13767db96d56Sopenharmony_ci 13777db96d56Sopenharmony_ci @mock.patch('builtins.print') 13787db96d56Sopenharmony_ci def test_server_test_localhost(self, _): 13797db96d56Sopenharmony_ci mock_server = self.mock_server_class() 13807db96d56Sopenharmony_ci server.test(ServerClass=mock_server, bind="localhost") 13817db96d56Sopenharmony_ci self.assertIn( 13827db96d56Sopenharmony_ci mock_server.address_family, 13837db96d56Sopenharmony_ci (socket.AF_INET6, socket.AF_INET), 13847db96d56Sopenharmony_ci ) 13857db96d56Sopenharmony_ci 13867db96d56Sopenharmony_ci ipv6_addrs = ( 13877db96d56Sopenharmony_ci "::", 13887db96d56Sopenharmony_ci "2001:0db8:85a3:0000:0000:8a2e:0370:7334", 13897db96d56Sopenharmony_ci "::1", 13907db96d56Sopenharmony_ci ) 13917db96d56Sopenharmony_ci 13927db96d56Sopenharmony_ci ipv4_addrs = ( 13937db96d56Sopenharmony_ci "0.0.0.0", 13947db96d56Sopenharmony_ci "8.8.8.8", 13957db96d56Sopenharmony_ci "127.0.0.1", 13967db96d56Sopenharmony_ci ) 13977db96d56Sopenharmony_ci 13987db96d56Sopenharmony_ci @mock.patch('builtins.print') 13997db96d56Sopenharmony_ci def test_server_test_ipv6(self, _): 14007db96d56Sopenharmony_ci for bind in self.ipv6_addrs: 14017db96d56Sopenharmony_ci mock_server = self.mock_server_class() 14027db96d56Sopenharmony_ci server.test(ServerClass=mock_server, bind=bind) 14037db96d56Sopenharmony_ci self.assertEqual(mock_server.address_family, socket.AF_INET6) 14047db96d56Sopenharmony_ci 14057db96d56Sopenharmony_ci @mock.patch('builtins.print') 14067db96d56Sopenharmony_ci def test_server_test_ipv4(self, _): 14077db96d56Sopenharmony_ci for bind in self.ipv4_addrs: 14087db96d56Sopenharmony_ci mock_server = self.mock_server_class() 14097db96d56Sopenharmony_ci server.test(ServerClass=mock_server, bind=bind) 14107db96d56Sopenharmony_ci self.assertEqual(mock_server.address_family, socket.AF_INET) 14117db96d56Sopenharmony_ci 14127db96d56Sopenharmony_ci 14137db96d56Sopenharmony_cidef setUpModule(): 14147db96d56Sopenharmony_ci unittest.addModuleCleanup(os.chdir, os.getcwd()) 14157db96d56Sopenharmony_ci 14167db96d56Sopenharmony_ci 14177db96d56Sopenharmony_ciif __name__ == '__main__': 14187db96d56Sopenharmony_ci unittest.main() 1419