@@ -1450,29 +1450,17 @@ def parse_http_list(s):
14501450 return [part .strip () for part in res ]
14511451
14521452class FileHandler (BaseHandler ):
1453- # names for the localhost
1454- names = None
1455- def get_names (self ):
1456- if FileHandler .names is None :
1457- try :
1458- FileHandler .names = tuple (
1459- socket .gethostbyname_ex ('localhost' )[2 ] +
1460- socket .gethostbyname_ex (socket .gethostname ())[2 ])
1461- except socket .gaierror :
1462- FileHandler .names = (socket .gethostbyname ('localhost' ),)
1463- return FileHandler .names
1464-
14651453 # not entirely sure what the rules are here
14661454 def open_local_file (self , req ):
14671455 import email .utils
14681456 import mimetypes
1469- filename = req .full_url
1470- localfile = url2pathname (filename . removeprefix ( 'file:' ) )
1457+ filename = _splittype ( req .full_url )[ 1 ]
1458+ localfile = url2pathname (filename )
14711459 try :
14721460 stats = os .stat (localfile )
14731461 size = stats .st_size
14741462 modified = email .utils .formatdate (stats .st_mtime , usegmt = True )
1475- mtype = mimetypes .guess_type ( filename )[0 ]
1463+ mtype = mimetypes .guess_file_type ( localfile )[0 ]
14761464 headers = email .message_from_string (
14771465 'Content-type: %s\n Content-length: %d\n Last-modified: %s\n ' %
14781466 (mtype or 'text/plain' , size , modified ))
@@ -1483,14 +1471,25 @@ def open_local_file(self, req):
14831471
14841472 file_open = open_local_file
14851473
1486- def _is_local_host (host ):
1487- if not host or host == 'localhost' :
1474+ _local_addresses = None
1475+
1476+ def _is_local_authority (authority ):
1477+ global _local_addresses
1478+
1479+ if not authority or authority == 'localhost' :
14881480 return True
14891481 try :
1490- name = socket .gethostbyname (host )
1482+ address = socket .gethostbyname (authority )
14911483 except socket .gaierror :
14921484 return False
1493- return name in FileHandler ().get_names ()
1485+ if _local_addresses is None :
1486+ try :
1487+ _local_addresses = tuple (
1488+ socket .gethostbyname_ex ('localhost' )[2 ] +
1489+ socket .gethostbyname_ex (socket .gethostname ())[2 ])
1490+ except socket .gaierror :
1491+ _local_addresses = (socket .gethostbyname ('localhost' ),)
1492+ return address in _local_addresses
14941493
14951494class FTPHandler (BaseHandler ):
14961495 def ftp_open (self , req ):
@@ -1639,12 +1638,12 @@ def url2pathname(url):
16391638 """OS-specific conversion from a relative URL of the 'file' scheme
16401639 to a file system path; not recommended for general use."""
16411640 authority , url = _splithost (url )
1642-
16431641 if os .name == 'nt' :
16441642 if authority and authority != 'localhost' :
1643+ # e.g. file://server/share/file.txt
16451644 url = '//' + authority + url
16461645 elif url [:3 ] == '///' :
1647- # Skip past extra slash before UNC drive in URL path.
1646+ # e.g. file://///server/share/file.txt
16481647 url = url [1 :]
16491648 else :
16501649 if url [:1 ] == '/' and url [2 :3 ] in (':' , '|' ):
@@ -1654,8 +1653,8 @@ def url2pathname(url):
16541653 # Older URLs use a pipe after a drive letter
16551654 url = url [:1 ] + ':' + url [2 :]
16561655 url = url .replace ('/' , '\\ ' )
1657- elif not _is_local_host (authority ):
1658- raise URLError (f'URL { url !r } uses non-local authority { authority !r } ' )
1656+ elif not _is_local_authority (authority ):
1657+ raise URLError ("file:// scheme is supported only on localhost" )
16591658 encoding = sys .getfilesystemencoding ()
16601659 errors = sys .getfilesystemencodeerrors ()
16611660 return unquote (url , encoding = encoding , errors = errors )
0 commit comments