@@ -522,34 +522,111 @@ def close_conn():
522
522
reader .close ()
523
523
return body
524
524
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
+
525
577
@unittest .skipIf (is_apple ,
526
578
'undecodable name cannot always be decoded on Apple platforms' )
527
579
@unittest .skipIf (sys .platform == 'win32' ,
528
580
'undecodable name cannot be decoded on win32' )
529
581
@unittest .skipUnless (os_helper .TESTFN_UNDECODABLE ,
530
582
'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 ):
533
594
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#' , '&' , '&' , '<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#' , '&' , '&' , '<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 ))
553
630
554
631
def test_undecodable_parameter (self ):
555
632
# sanity check using a valid parameter
@@ -731,27 +808,6 @@ def test_path_without_leading_slash(self):
731
808
self .assertEqual (response .getheader ("Location" ),
732
809
self .tempdir_name + "/?hi=1" )
733
810
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
-
755
811
756
812
cgi_file1 = """\
757
813
#!%s
0 commit comments