# HG changeset patch # Parent 37ed61b1234a8e7375e7e4d5d7561ad5dcf427cb diff -r 37ed61b1234a Lib/test/test_urllib2.py --- a/Lib/test/test_urllib2.py Tue May 26 20:46:29 2015 -0700 +++ b/Lib/test/test_urllib2.py Wed May 27 05:08:58 2015 +0000 @@ -2,7 +2,7 @@ from test import support from test import test_urllib -import os +import os, os.path import io import socket import array @@ -750,15 +750,15 @@ def test_file(self): import email.utils h = urllib.request.FileHandler() - o = h.parent = MockOpener() TESTFN = support.TESTFN urlpath = sanepathname2url(os.path.abspath(TESTFN)) towrite = b"hello, world\n" + localhost = socket.gethostbyname('localhost') urls = [ "file://localhost%s" % urlpath, "file://%s" % urlpath, - "file://%s%s" % (socket.gethostbyname('localhost'), urlpath), + "file://%s%s" % (localhost, urlpath), ] try: localaddr = socket.gethostbyname(socket.gethostname()) @@ -767,31 +767,42 @@ if localaddr: urls.append("file://%s%s" % (localaddr, urlpath)) + # Try unusual host string because host should be checked by address + if socket.gethostbyname("127.1") == localhost: + # Test path component beginning with exactly two slashes, because + # old code was looking for a host name in the wrong place + double_slash = "/" + urlpath + self.assertTrue(double_slash.startswith("//")) + self.assertTrue(not double_slash.startswith("///")) + urls.append("file://127.1" + double_slash) + for url in urls: - f = open(TESTFN, "wb") - try: + with self.subTest(url): + f = open(TESTFN, "wb") try: - f.write(towrite) + with f: + f.write(towrite) + + r = h.file_open(Request(url)) + try: + data = r.read() + headers = r.info() + respurl = r.geturl() + finally: + r.close() + stats = os.stat(TESTFN) + modified = email.utils.formatdate(stats.st_mtime, usegmt=True) finally: - f.close() + os.remove(TESTFN) + self.assertEqual(data, towrite) + self.assertEqual(headers["Content-type"], "text/plain") + self.assertEqual(headers["Content-length"], "13") + self.assertEqual(headers["Last-modified"], modified) + self.assertEqual(respurl, url) - r = h.file_open(Request(url)) - try: - data = r.read() - headers = r.info() - respurl = r.geturl() - finally: - r.close() - stats = os.stat(TESTFN) - modified = email.utils.formatdate(stats.st_mtime, usegmt=True) - finally: - os.remove(TESTFN) - self.assertEqual(data, towrite) - self.assertEqual(headers["Content-type"], "text/plain") - self.assertEqual(headers["Content-length"], "13") - self.assertEqual(headers["Last-modified"], modified) - self.assertEqual(respurl, url) - + self.addCleanup(os.remove, TESTFN) + with open(TESTFN, "wb"): + pass # Just make sure the file exists for url in [ "file://localhost:80%s" % urlpath, "file:///file_does_not_exist.txt", @@ -801,45 +812,33 @@ "file://somerandomhost.ontheinternet.com%s/%s" % (os.getcwd(), TESTFN), ]: - try: - f = open(TESTFN, "wb") - try: - f.write(towrite) - finally: - f.close() - + with self.subTest(url): self.assertRaises(urllib.error.URLError, h.file_open, Request(url)) - finally: - os.remove(TESTFN) + def test_file_ftp(self): + # Some of the following URLs used to be interpreted as FTP URLs h = urllib.request.FileHandler() - o = h.parent = MockOpener() - # XXXX why does // mean ftp (and /// mean not ftp!), and where - # is file: scheme specified? I think this is really a bug, and - # what was intended was to distinguish between URLs like: - # file:/blah.txt (a file) - # file://localhost/blah.txt (a file) - # file:///blah.txt (a file) - # file://ftp.example.com/blah.txt (an ftp URL) - for url, ftp in [ - ("file://ftp.example.com//foo.txt", False), - ("file://ftp.example.com///foo.txt", False), -# XXXX bug: fails with OSError, should be URLError - ("file://ftp.example.com/foo.txt", False), - ("file://somehost//foo/something.txt", False), - ("file://localhost//foo/something.txt", False), + for url in [ + "file://ftp.example.com//foo.txt", + "file://ftp.example.com///foo.txt", + "file://ftp.example.com/foo.txt", + "file://somehost//foo/something.txt", + "file://localhost//foo/something.txt", ]: - req = Request(url) - try: - h.file_open(req) - # XXXX remove OSError when bug fixed - except (urllib.error.URLError, OSError): - self.assertFalse(ftp) - else: - self.assertIs(o.req, req) - self.assertEqual(req.type, "ftp") - self.assertEqual(req.type == "ftp", ftp) + with self.subTest(url): + req = Request(url) + self.assertRaises(urllib.error.URLError, h.file_open, req) + self.assertEqual(req.type, "file") + + def test_file_remote_error(self): + # Local file system should not influence error for remote host + file = os.path.abspath(support.TESTFN) + self.assertFalse(os.path.exists(file)) + url = "file://python.org" + sanepathname2url(file) + expected = "file not on local host" + with self.assertRaisesRegex(urllib.error.URLError, expected): + urllib.request.urlopen(url) def test_http(self): diff -r 37ed61b1234a Lib/urllib/request.py --- a/Lib/urllib/request.py Tue May 26 20:46:29 2015 -0700 +++ b/Lib/urllib/request.py Wed May 27 05:08:58 2015 +0000 @@ -1362,15 +1362,9 @@ return [part.strip() for part in res] class FileHandler(BaseHandler): - # Use local file or FTP depending on form of URL + # Use local file only; host must be equivalent to localhost def file_open(self, req): - url = req.selector - if url[:2] == '//' and url[2:3] != '/' and (req.host and - req.host != 'localhost'): - if not req.host in self.get_names(): - raise URLError("file:// scheme is supported only on localhost") - else: - return self.open_local_file(req) + return self.open_local_file(req) # names for the localhost names = None @@ -1388,7 +1382,14 @@ def open_local_file(self, req): import email.utils import mimetypes + host = req.host + if host: + host, port = splitport(host) + if host and (port or + _safe_gethostbyname(host) not in self.get_names()): + raise URLError('file not on local host') + filename = req.selector localfile = url2pathname(filename) try: @@ -1400,18 +1401,13 @@ 'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' % (mtype or 'text/plain', size, modified)) if host: - host, port = splitport(host) - if not host or \ - (not port and _safe_gethostbyname(host) in self.get_names()): - if host: - origurl = 'file://' + host + filename - else: - origurl = 'file://' + filename - return addinfourl(open(localfile, 'rb'), headers, origurl) + origurl = 'file://' + host + filename + else: + origurl = 'file://' + filename + return addinfourl(open(localfile, 'rb'), headers, origurl) except OSError as exp: # users shouldn't expect OSErrors coming from urlopen() raise URLError(exp) - raise URLError('file not on local host') def _safe_gethostbyname(host): try: