diff -r 461afc24fabc Lib/test/test_urllib2.py --- a/Lib/test/test_urllib2.py Tue Mar 03 20:21:35 2015 +0200 +++ b/Lib/test/test_urllib2.py Tue Mar 03 20:22:56 2015 +0200 @@ -660,6 +660,12 @@ class HandlerTests(unittest.TestCase): ("ftp://localhost:80/foo/bar/", "localhost", 80, "", "", "D", ["foo", "bar"], "", None), + ("ftp://localhost:/foo/bar/baz.html", + "localhost", ftplib.FTP_PORT, "", "", "I", + ["foo", "bar"], "baz.html", "text/html"), + ("ftp://parrot:123@localhost:80/foo/bar/baz.html", + "localhost", 80, "parrot", "123", "I", + ["foo", "bar"], "baz.html", "text/html"), ("ftp://localhost/baz.gif;type=a", "localhost", ftplib.FTP_PORT, "", "", "A", [], "baz.gif", None), # XXX really this should guess image/gif diff -r 461afc24fabc Lib/test/test_urlparse.py --- a/Lib/test/test_urlparse.py Tue Mar 03 20:21:35 2015 +0200 +++ b/Lib/test/test_urlparse.py Tue Mar 03 20:22:56 2015 +0200 @@ -552,28 +552,23 @@ class UrlParseTestCase(unittest.TestCase self.assertEqual(p.geturl(), url) # Verify an illegal port is returned as None - url = b"HTTP://WWW.PYTHON.ORG:65536/doc/#frag" - p = urllib.parse.urlsplit(url) - self.assertEqual(p.port, None) + + with self.assertRaises(ValueError) as cm: + urllib.parse.urlsplit(b"HTTP://WWW.PYTHON.ORG:65536/doc/#frag") + self.assertRegex(str(cm.exception), '^Invalid port number: ') def test_attributes_bad_port(self): """Check handling of non-integer ports.""" - p = urllib.parse.urlsplit("http://www.example.net:foo") - self.assertEqual(p.netloc, "www.example.net:foo") - self.assertRaises(ValueError, lambda: p.port) - - p = urllib.parse.urlparse("http://www.example.net:foo") - self.assertEqual(p.netloc, "www.example.net:foo") - self.assertRaises(ValueError, lambda: p.port) + self.assertRaises(ValueError, + urllib.parse.urlsplit, "http://www.example.net:foo") + self.assertRaises(ValueError, + urllib.parse.urlparse, "http://www.example.net:foo") # Once again, repeat ourselves to test bytes - p = urllib.parse.urlsplit(b"http://www.example.net:foo") - self.assertEqual(p.netloc, b"www.example.net:foo") - self.assertRaises(ValueError, lambda: p.port) - - p = urllib.parse.urlparse(b"http://www.example.net:foo") - self.assertEqual(p.netloc, b"www.example.net:foo") - self.assertRaises(ValueError, lambda: p.port) + self.assertRaises(ValueError, + urllib.parse.urlsplit, b"http://www.example.net:foo") + self.assertRaises(ValueError, + urllib.parse.urlsplit, b"http://www.example.net:foo") def test_attributes_without_netloc(self): # This example is straight from RFC 3261. It looks like it @@ -915,6 +910,9 @@ class Utility_Tests(unittest.TestCase): self.assertEqual(splitnport('127.0.0.1', 55), ('127.0.0.1', 55)) self.assertEqual(splitnport('parrot:cheese'), ('parrot', None)) self.assertEqual(splitnport('parrot:cheese', 55), ('parrot', None)) + self.assertEqual(splitnport('[::1]:80'), ('[::1]', 80)) + self.assertEqual(splitnport('[::1]'), ('[::1]', -1)) + self.assertEqual(splitnport('[::1]:'), ('[::1]', -1)) def test_splitquery(self): # Normal cases are exercised by other tests; ensure that we also diff -r 461afc24fabc Lib/urllib/parse.py --- a/Lib/urllib/parse.py Tue Mar 03 20:21:35 2015 +0200 +++ b/Lib/urllib/parse.py Tue Mar 03 20:22:56 2015 +0200 @@ -145,7 +145,7 @@ class _NetlocResultMixinBase(object): hostname = self._hostinfo[0] if not hostname: hostname = None - elif hostname is not None: + else: hostname = hostname.lower() return hostname @@ -314,7 +314,17 @@ def _splitnetloc(url, start=0): wdelim = url.find(c, start) # find first of this delim if wdelim >= 0: # if found delim = min(delim, wdelim) # use earliest delim position - return url[start:delim], url[delim:] # return (domain, rest) + netloc = url[start:delim] + url = url[delim:] + user, host = splituser(netloc) + host, port = splitnport(host) + if host[:1] == '[' and host[-1:] == ']': + host = host[1:-1] + if '[' in host or ']' in host: + raise ValueError('Invalid IPv6 URL') + if port is None: + raise ValueError('Invalid port number: %r' % port) + return netloc, url def urlsplit(url, scheme='', allow_fragments=True): """Parse a URL into 5 components: @@ -338,9 +348,6 @@ def urlsplit(url, scheme='', allow_fragm url = url[i+1:] if url[:2] == '//': netloc, url = _splitnetloc(url, 2) - if (('[' in netloc and ']' not in netloc) or - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") if allow_fragments and '#' in url: url, fragment = url.split('#', 1) if '?' in url: @@ -361,9 +368,6 @@ def urlsplit(url, scheme='', allow_fragm if url[:2] == '//': netloc, url = _splitnetloc(url, 2) - if (('[' in netloc and ']' not in netloc) or - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") if allow_fragments and '#' in url: url, fragment = url.split('#', 1) if '?' in url: @@ -922,15 +926,18 @@ def splitnport(host, defport=-1): Return given default port if no ':' found; defaults to -1. Return numerical port if a valid number are found after ':'. Return None if ':' but not a valid number.""" - host, delim, port = host.rpartition(':') - if not delim: - host = port - elif port: - try: - nport = int(port) - except ValueError: - nport = None - return host, nport + if host[-1:] != ']': + host, delim, port = host.rpartition(':') + if not delim: + host = port + elif port: + try: + nport = int(port) + if not (1 <= nport <= 65535): + nport = None + except ValueError: + nport = None + return host, nport return host, defport def splitquery(url): diff -r 461afc24fabc Lib/urllib/request.py --- a/Lib/urllib/request.py Tue Mar 03 20:21:35 2015 +0200 +++ b/Lib/urllib/request.py Tue Mar 03 20:22:56 2015 +0200 @@ -1384,18 +1384,18 @@ class FTPHandler(BaseHandler): host = req.host if not host: raise URLError('ftp error: no host given') - host, port = splitport(host) - if port is None: - port = ftplib.FTP_PORT - else: - port = int(port) - # username/password handling user, host = splituser(host) if user: user, passwd = splitpasswd(user) else: passwd = None + + host, port = splitport(host) + if port is None: + port = ftplib.FTP_PORT + else: + port = int(port) host = unquote(host) user = user or '' passwd = passwd or '' @@ -1898,8 +1898,8 @@ class URLopener: import mimetypes host, path = splithost(url) if not host: raise URLError('ftp error: no host given') + user, host = splituser(host) host, port = splitport(host) - user, host = splituser(host) if user: user, passwd = splitpasswd(user) else: passwd = None host = unquote(host)