Skip to content

Commit 6dae082

Browse files
[3.13] gh-133889: Improve tests for SimpleHTTPRequestHandler (GH-134102) (GH-134122)
(cherry picked from commit fcaf009) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 510e28e commit 6dae082

File tree

1 file changed

+98
-42
lines changed

1 file changed

+98
-42
lines changed

Lib/test/test_httpservers.py

Lines changed: 98 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -411,34 +411,111 @@ def close_conn():
411411
reader.close()
412412
return body
413413

414+
def check_list_dir_dirname(self, dirname, quotedname=None):
415+
fullpath = os.path.join(self.tempdir, dirname)
416+
try:
417+
os.mkdir(os.path.join(self.tempdir, dirname))
418+
except (OSError, UnicodeEncodeError):
419+
self.skipTest(f'Can not create directory {dirname!a} '
420+
f'on current file system')
421+
422+
if quotedname is None:
423+
quotedname = urllib.parse.quote(dirname, errors='surrogatepass')
424+
response = self.request(self.base_url + '/' + quotedname + '/')
425+
body = self.check_status_and_reason(response, HTTPStatus.OK)
426+
displaypath = html.escape(f'{self.base_url}/{dirname}/', quote=False)
427+
enc = sys.getfilesystemencoding()
428+
prefix = f'listing for {displaypath}</'.encode(enc, 'surrogateescape')
429+
self.assertIn(prefix + b'title>', body)
430+
self.assertIn(prefix + b'h1>', body)
431+
432+
def check_list_dir_filename(self, filename):
433+
fullpath = os.path.join(self.tempdir, filename)
434+
content = ascii(fullpath).encode() + (os_helper.TESTFN_UNDECODABLE or b'\xff')
435+
try:
436+
with open(fullpath, 'wb') as f:
437+
f.write(content)
438+
except OSError:
439+
self.skipTest(f'Can not create file {filename!a} '
440+
f'on current file system')
441+
442+
response = self.request(self.base_url + '/')
443+
body = self.check_status_and_reason(response, HTTPStatus.OK)
444+
quotedname = urllib.parse.quote(filename, errors='surrogatepass')
445+
enc = response.headers.get_content_charset()
446+
self.assertIsNotNone(enc)
447+
self.assertIn((f'href="{quotedname}"').encode('ascii'), body)
448+
displayname = html.escape(filename, quote=False)
449+
self.assertIn(f'>{displayname}<'.encode(enc, 'surrogateescape'), body)
450+
451+
response = self.request(self.base_url + '/' + quotedname)
452+
self.check_status_and_reason(response, HTTPStatus.OK, data=content)
453+
454+
@unittest.skipUnless(os_helper.TESTFN_NONASCII,
455+
'need os_helper.TESTFN_NONASCII')
456+
def test_list_dir_nonascii_dirname(self):
457+
dirname = os_helper.TESTFN_NONASCII + '.dir'
458+
self.check_list_dir_dirname(dirname)
459+
460+
@unittest.skipUnless(os_helper.TESTFN_NONASCII,
461+
'need os_helper.TESTFN_NONASCII')
462+
def test_list_dir_nonascii_filename(self):
463+
filename = os_helper.TESTFN_NONASCII + '.txt'
464+
self.check_list_dir_filename(filename)
465+
414466
@unittest.skipIf(is_apple,
415467
'undecodable name cannot always be decoded on Apple platforms')
416468
@unittest.skipIf(sys.platform == 'win32',
417469
'undecodable name cannot be decoded on win32')
418470
@unittest.skipUnless(os_helper.TESTFN_UNDECODABLE,
419471
'need os_helper.TESTFN_UNDECODABLE')
420-
def test_undecodable_filename(self):
421-
enc = sys.getfilesystemencoding()
472+
def test_list_dir_undecodable_dirname(self):
473+
dirname = os.fsdecode(os_helper.TESTFN_UNDECODABLE) + '.dir'
474+
self.check_list_dir_dirname(dirname)
475+
476+
@unittest.skipIf(is_apple,
477+
'undecodable name cannot always be decoded on Apple platforms')
478+
@unittest.skipIf(sys.platform == 'win32',
479+
'undecodable name cannot be decoded on win32')
480+
@unittest.skipUnless(os_helper.TESTFN_UNDECODABLE,
481+
'need os_helper.TESTFN_UNDECODABLE')
482+
def test_list_dir_undecodable_filename(self):
422483
filename = os.fsdecode(os_helper.TESTFN_UNDECODABLE) + '.txt'
423-
with open(os.path.join(self.tempdir, filename), 'wb') as f:
424-
f.write(os_helper.TESTFN_UNDECODABLE)
425-
response = self.request(self.base_url + '/')
426-
if is_apple:
427-
# On Apple platforms the HFS+ filesystem replaces bytes that
428-
# aren't valid UTF-8 into a percent-encoded value.
429-
for name in os.listdir(self.tempdir):
430-
if name != 'test': # Ignore a filename created in setUp().
431-
filename = name
432-
break
433-
body = self.check_status_and_reason(response, HTTPStatus.OK)
434-
quotedname = urllib.parse.quote(filename, errors='surrogatepass')
435-
self.assertIn(('href="%s"' % quotedname)
436-
.encode(enc, 'surrogateescape'), body)
437-
self.assertIn(('>%s<' % html.escape(filename, quote=False))
438-
.encode(enc, 'surrogateescape'), body)
439-
response = self.request(self.base_url + '/' + quotedname)
440-
self.check_status_and_reason(response, HTTPStatus.OK,
441-
data=os_helper.TESTFN_UNDECODABLE)
484+
self.check_list_dir_filename(filename)
485+
486+
def test_list_dir_undecodable_dirname2(self):
487+
dirname = '\ufffd.dir'
488+
self.check_list_dir_dirname(dirname, quotedname='%ff.dir')
489+
490+
@unittest.skipUnless(os_helper.TESTFN_UNENCODABLE,
491+
'need os_helper.TESTFN_UNENCODABLE')
492+
def test_list_dir_unencodable_dirname(self):
493+
dirname = os_helper.TESTFN_UNENCODABLE + '.dir'
494+
self.check_list_dir_dirname(dirname)
495+
496+
@unittest.skipUnless(os_helper.TESTFN_UNENCODABLE,
497+
'need os_helper.TESTFN_UNENCODABLE')
498+
def test_list_dir_unencodable_filename(self):
499+
filename = os_helper.TESTFN_UNENCODABLE + '.txt'
500+
self.check_list_dir_filename(filename)
501+
502+
def test_list_dir_escape_dirname(self):
503+
# Characters that need special treating in URL or HTML.
504+
for name in ('q?', 'f#', '&amp;', '&amp', '<i>', '"dq"', "'sq'",
505+
'%A4', '%E2%82%AC'):
506+
with self.subTest(name=name):
507+
dirname = name + '.dir'
508+
self.check_list_dir_dirname(dirname,
509+
quotedname=urllib.parse.quote(dirname, safe='&<>\'"'))
510+
511+
def test_list_dir_escape_filename(self):
512+
# Characters that need special treating in URL or HTML.
513+
for name in ('q?', 'f#', '&amp;', '&amp', '<i>', '"dq"', "'sq'",
514+
'%A4', '%E2%82%AC'):
515+
with self.subTest(name=name):
516+
filename = name + '.txt'
517+
self.check_list_dir_filename(filename)
518+
os_helper.unlink(os.path.join(self.tempdir, filename))
442519

443520
def test_undecodable_parameter(self):
444521
# sanity check using a valid parameter
@@ -620,27 +697,6 @@ def test_path_without_leading_slash(self):
620697
self.assertEqual(response.getheader("Location"),
621698
self.tempdir_name + "/?hi=1")
622699

623-
def test_html_escape_filename(self):
624-
filename = '<test&>.txt'
625-
fullpath = os.path.join(self.tempdir, filename)
626-
627-
try:
628-
open(fullpath, 'wb').close()
629-
except OSError:
630-
raise unittest.SkipTest('Can not create file %s on current file '
631-
'system' % filename)
632-
633-
try:
634-
response = self.request(self.base_url + '/')
635-
body = self.check_status_and_reason(response, HTTPStatus.OK)
636-
enc = response.headers.get_content_charset()
637-
finally:
638-
os.unlink(fullpath) # avoid affecting test_undecodable_filename
639-
640-
self.assertIsNotNone(enc)
641-
html_text = '>%s<' % html.escape(filename, quote=False)
642-
self.assertIn(html_text.encode(enc), body)
643-
644700

645701
cgi_file1 = """\
646702
#!%s

0 commit comments

Comments
 (0)