Skip to content

Commit 5cdad8c

Browse files
[3.14] gh-133889: Improve tests for SimpleHTTPRequestHandler (GH-134102) (GH-134121)
(cherry picked from commit fcaf009) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent f893232 commit 5cdad8c

File tree

1 file changed

+98
-42
lines changed

1 file changed

+98
-42
lines changed

Lib/test/test_httpservers.py

+98-42
Original file line numberDiff line numberDiff line change
@@ -522,34 +522,111 @@ def close_conn():
522522
reader.close()
523523
return body
524524

525+
def check_list_dir_dirname(self, dirname, quotedname=None):
526+
fullpath = os.path.join(self.tempdir, dirname)
527+
try:
528+
os.mkdir(os.path.join(self.tempdir, dirname))
529+
except (OSError, UnicodeEncodeError):
530+
self.skipTest(f'Can not create directory {dirname!a} '
531+
f'on current file system')
532+
533+
if quotedname is None:
534+
quotedname = urllib.parse.quote(dirname, errors='surrogatepass')
535+
response = self.request(self.base_url + '/' + quotedname + '/')
536+
body = self.check_status_and_reason(response, HTTPStatus.OK)
537+
displaypath = html.escape(f'{self.base_url}/{dirname}/', quote=False)
538+
enc = sys.getfilesystemencoding()
539+
prefix = f'listing for {displaypath}</'.encode(enc, 'surrogateescape')
540+
self.assertIn(prefix + b'title>', body)
541+
self.assertIn(prefix + b'h1>', body)
542+
543+
def check_list_dir_filename(self, filename):
544+
fullpath = os.path.join(self.tempdir, filename)
545+
content = ascii(fullpath).encode() + (os_helper.TESTFN_UNDECODABLE or b'\xff')
546+
try:
547+
with open(fullpath, 'wb') as f:
548+
f.write(content)
549+
except OSError:
550+
self.skipTest(f'Can not create file {filename!a} '
551+
f'on current file system')
552+
553+
response = self.request(self.base_url + '/')
554+
body = self.check_status_and_reason(response, HTTPStatus.OK)
555+
quotedname = urllib.parse.quote(filename, errors='surrogatepass')
556+
enc = response.headers.get_content_charset()
557+
self.assertIsNotNone(enc)
558+
self.assertIn((f'href="{quotedname}"').encode('ascii'), body)
559+
displayname = html.escape(filename, quote=False)
560+
self.assertIn(f'>{displayname}<'.encode(enc, 'surrogateescape'), body)
561+
562+
response = self.request(self.base_url + '/' + quotedname)
563+
self.check_status_and_reason(response, HTTPStatus.OK, data=content)
564+
565+
@unittest.skipUnless(os_helper.TESTFN_NONASCII,
566+
'need os_helper.TESTFN_NONASCII')
567+
def test_list_dir_nonascii_dirname(self):
568+
dirname = os_helper.TESTFN_NONASCII + '.dir'
569+
self.check_list_dir_dirname(dirname)
570+
571+
@unittest.skipUnless(os_helper.TESTFN_NONASCII,
572+
'need os_helper.TESTFN_NONASCII')
573+
def test_list_dir_nonascii_filename(self):
574+
filename = os_helper.TESTFN_NONASCII + '.txt'
575+
self.check_list_dir_filename(filename)
576+
525577
@unittest.skipIf(is_apple,
526578
'undecodable name cannot always be decoded on Apple platforms')
527579
@unittest.skipIf(sys.platform == 'win32',
528580
'undecodable name cannot be decoded on win32')
529581
@unittest.skipUnless(os_helper.TESTFN_UNDECODABLE,
530582
'need os_helper.TESTFN_UNDECODABLE')
531-
def test_undecodable_filename(self):
532-
enc = sys.getfilesystemencoding()
583+
def test_list_dir_undecodable_dirname(self):
584+
dirname = os.fsdecode(os_helper.TESTFN_UNDECODABLE) + '.dir'
585+
self.check_list_dir_dirname(dirname)
586+
587+
@unittest.skipIf(is_apple,
588+
'undecodable name cannot always be decoded on Apple platforms')
589+
@unittest.skipIf(sys.platform == 'win32',
590+
'undecodable name cannot be decoded on win32')
591+
@unittest.skipUnless(os_helper.TESTFN_UNDECODABLE,
592+
'need os_helper.TESTFN_UNDECODABLE')
593+
def test_list_dir_undecodable_filename(self):
533594
filename = os.fsdecode(os_helper.TESTFN_UNDECODABLE) + '.txt'
534-
with open(os.path.join(self.tempdir, filename), 'wb') as f:
535-
f.write(os_helper.TESTFN_UNDECODABLE)
536-
response = self.request(self.base_url + '/')
537-
if is_apple:
538-
# On Apple platforms the HFS+ filesystem replaces bytes that
539-
# aren't valid UTF-8 into a percent-encoded value.
540-
for name in os.listdir(self.tempdir):
541-
if name != 'test': # Ignore a filename created in setUp().
542-
filename = name
543-
break
544-
body = self.check_status_and_reason(response, HTTPStatus.OK)
545-
quotedname = urllib.parse.quote(filename, errors='surrogatepass')
546-
self.assertIn(('href="%s"' % quotedname)
547-
.encode(enc, 'surrogateescape'), body)
548-
self.assertIn(('>%s<' % html.escape(filename, quote=False))
549-
.encode(enc, 'surrogateescape'), body)
550-
response = self.request(self.base_url + '/' + quotedname)
551-
self.check_status_and_reason(response, HTTPStatus.OK,
552-
data=os_helper.TESTFN_UNDECODABLE)
595+
self.check_list_dir_filename(filename)
596+
597+
def test_list_dir_undecodable_dirname2(self):
598+
dirname = '\ufffd.dir'
599+
self.check_list_dir_dirname(dirname, quotedname='%ff.dir')
600+
601+
@unittest.skipUnless(os_helper.TESTFN_UNENCODABLE,
602+
'need os_helper.TESTFN_UNENCODABLE')
603+
def test_list_dir_unencodable_dirname(self):
604+
dirname = os_helper.TESTFN_UNENCODABLE + '.dir'
605+
self.check_list_dir_dirname(dirname)
606+
607+
@unittest.skipUnless(os_helper.TESTFN_UNENCODABLE,
608+
'need os_helper.TESTFN_UNENCODABLE')
609+
def test_list_dir_unencodable_filename(self):
610+
filename = os_helper.TESTFN_UNENCODABLE + '.txt'
611+
self.check_list_dir_filename(filename)
612+
613+
def test_list_dir_escape_dirname(self):
614+
# Characters that need special treating in URL or HTML.
615+
for name in ('q?', 'f#', '&amp;', '&amp', '<i>', '"dq"', "'sq'",
616+
'%A4', '%E2%82%AC'):
617+
with self.subTest(name=name):
618+
dirname = name + '.dir'
619+
self.check_list_dir_dirname(dirname,
620+
quotedname=urllib.parse.quote(dirname, safe='&<>\'"'))
621+
622+
def test_list_dir_escape_filename(self):
623+
# Characters that need special treating in URL or HTML.
624+
for name in ('q?', 'f#', '&amp;', '&amp', '<i>', '"dq"', "'sq'",
625+
'%A4', '%E2%82%AC'):
626+
with self.subTest(name=name):
627+
filename = name + '.txt'
628+
self.check_list_dir_filename(filename)
629+
os_helper.unlink(os.path.join(self.tempdir, filename))
553630

554631
def test_undecodable_parameter(self):
555632
# sanity check using a valid parameter
@@ -731,27 +808,6 @@ def test_path_without_leading_slash(self):
731808
self.assertEqual(response.getheader("Location"),
732809
self.tempdir_name + "/?hi=1")
733810

734-
def test_html_escape_filename(self):
735-
filename = '<test&>.txt'
736-
fullpath = os.path.join(self.tempdir, filename)
737-
738-
try:
739-
open(fullpath, 'wb').close()
740-
except OSError:
741-
raise unittest.SkipTest('Can not create file %s on current file '
742-
'system' % filename)
743-
744-
try:
745-
response = self.request(self.base_url + '/')
746-
body = self.check_status_and_reason(response, HTTPStatus.OK)
747-
enc = response.headers.get_content_charset()
748-
finally:
749-
os.unlink(fullpath) # avoid affecting test_undecodable_filename
750-
751-
self.assertIsNotNone(enc)
752-
html_text = '>%s<' % html.escape(filename, quote=False)
753-
self.assertIn(html_text.encode(enc), body)
754-
755811

756812
cgi_file1 = """\
757813
#!%s

0 commit comments

Comments
 (0)