@@ -411,34 +411,111 @@ def close_conn():
411
411
reader .close ()
412
412
return body
413
413
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
+
414
466
@unittest .skipIf (is_apple ,
415
467
'undecodable name cannot always be decoded on Apple platforms' )
416
468
@unittest .skipIf (sys .platform == 'win32' ,
417
469
'undecodable name cannot be decoded on win32' )
418
470
@unittest .skipUnless (os_helper .TESTFN_UNDECODABLE ,
419
471
'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 ):
422
483
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#' ,'&' ,'&' ,'<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#' ,'&' ,'&' ,'<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 ))
442
519
443
520
def test_undecodable_parameter (self ):
444
521
# sanity check using a valid parameter
@@ -620,27 +697,6 @@ def test_path_without_leading_slash(self):
620
697
self .assertEqual (response .getheader ("Location" ),
621
698
self .tempdir_name + "/?hi=1" )
622
699
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
-
644
700
645
701
cgi_file1 = """\
646
702
#!%s