Skip to content

Commit 802a54b

Browse files
miguendesorsenthil
authored andcommitted
Move port validation logic to parsing time
1 parent 7d5dce9 commit 802a54b

4 files changed

Lines changed: 41 additions & 30 deletions

File tree

‎Lib/test/test_urllib2.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -915,9 +915,9 @@ def test_file(self):
915915
parsed._replace(netloc='localhost:80').geturl(),
916916
"file:///file_does_not_exist.txt",
917917
"file://not-a-local-host.com//dir/file.txt",
918-
"file://%s:80%s/%s" % (socket.gethostbyname('localhost'),
918+
"file://%s:80/%s/%s" % (socket.gethostbyname('localhost'),
919919
os.getcwd(), TESTFN),
920-
"file://somerandomhost.ontheinternet.com%s/%s" %
920+
"file://somerandomhost.ontheinternet.com/%s/%s" %
921921
(os.getcwd(), TESTFN),
922922
]:
923923
try:

‎Lib/test/test_urlparse.py‎

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -917,9 +917,8 @@ def test_urlsplit_attributes(self):
917917

918918
# Verify an illegal port raises ValueError
919919
url = b"HTTP://WWW.PYTHON.ORG:65536/doc/#frag"
920-
p = urllib.parse.urlsplit(url)
921920
with self.assertRaisesRegex(ValueError, "out of range"):
922-
p.port
921+
urllib.parse.urlsplit(url)
923922

924923
def test_urlsplit_remove_unsafe_bytes(self):
925924
# Remove ASCII tabs and newlines from input
@@ -1029,10 +1028,8 @@ def test_attributes_bad_port(self, bytes, parse, port):
10291028
self.skipTest('non-ASCII bytes')
10301029
netloc = str_encode(netloc)
10311030
url = str_encode(url)
1032-
p = parse(url)
1033-
self.assertEqual(p.netloc, netloc)
10341031
with self.assertRaises(ValueError):
1035-
p.port
1032+
parse(url)
10361033

10371034
@support.subTests('bytes', (False, True))
10381035
@support.subTests('parse', (urllib.parse.urlsplit, urllib.parse.urlparse))
@@ -1670,13 +1667,11 @@ def test_splitting_bracketed_hosts(self):
16701667

16711668
def test_port_casting_failure_message(self):
16721669
message = "Port could not be cast to integer value as 'oracle'"
1673-
p1 = urllib.parse.urlparse('http://Server=sde; Service=sde:oracle')
16741670
with self.assertRaisesRegex(ValueError, message):
1675-
p1.port
1671+
urllib.parse.urlparse('http://Server=sde; Service=sde:oracle')
16761672

1677-
p2 = urllib.parse.urlsplit('http://Server=sde; Service=sde:oracle')
16781673
with self.assertRaisesRegex(ValueError, message):
1679-
p2.port
1674+
urllib.parse.urlsplit('http://Server=sde; Service=sde:oracle')
16801675

16811676
def test_telurl_params(self):
16821677
p1 = urllib.parse.urlparse('tel:123-4;phone-context=+1-650-516')

‎Lib/urllib/parse.py‎

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,27 @@ def _coerce_args(*args):
140140
return args + (_noop,)
141141
return _decode_args(args) + (_encode_result,)
142142

143+
def _parse_hostinfo(netloc):
144+
_, _, hostinfo = netloc.rpartition('@')
145+
_, have_open_br, bracketed = hostinfo.partition('[')
146+
if have_open_br:
147+
hostname, _, port = bracketed.partition(']')
148+
_, _, port = port.partition(':')
149+
else:
150+
hostname, _, port = hostinfo.partition(':')
151+
if not port:
152+
port = None
153+
return hostname, port
154+
155+
def _validate_port(port):
156+
if port.isdigit() and port.isascii():
157+
port = int(port)
158+
else:
159+
raise ValueError(f"Port could not be cast to integer value as {port!r}")
160+
if not (0 <= port <= 65535):
161+
raise ValueError("Port out of range 0-65535")
162+
return port
163+
143164
# Result objects are more helpful than simple tuples
144165
class _ResultMixinStr(object):
145166
"""Standard approach to encoding parsed results from str to bytes"""
@@ -198,12 +219,7 @@ def hostname(self):
198219
def port(self):
199220
port = self._hostinfo[1]
200221
if port is not None:
201-
if port.isdigit() and port.isascii():
202-
port = int(port)
203-
else:
204-
raise ValueError(f"Port could not be cast to integer value as {port!r}")
205-
if not (0 <= port <= 65535):
206-
raise ValueError("Port out of range 0-65535")
222+
port = _validate_port(port)
207223
return port
208224

209225
__class_getitem__ = classmethod(types.GenericAlias)
@@ -231,16 +247,7 @@ def _hostinfo(self):
231247
netloc = self.netloc
232248
if netloc is None:
233249
return None, None
234-
_, _, hostinfo = netloc.rpartition('@')
235-
_, have_open_br, bracketed = hostinfo.partition('[')
236-
if have_open_br:
237-
hostname, _, port = bracketed.partition(']')
238-
_, _, port = port.partition(':')
239-
else:
240-
hostname, _, port = hostinfo.partition(':')
241-
if not port:
242-
port = None
243-
return hostname, port
250+
return _parse_hostinfo(netloc)
244251

245252

246253
class _NetlocResultMixinBytes(_NetlocResultMixinBase, _ResultMixinBytes):
@@ -506,9 +513,7 @@ def _splitnetloc(url, start=0):
506513
delim = min(delim, wdelim) # use earliest delim position
507514
return url[start:delim], url[delim:] # return (domain, rest)
508515

509-
def _checknetloc(netloc):
510-
if not netloc or netloc.isascii():
511-
return
516+
def _checknetloc_nfkc(netloc):
512517
# looking for characters like \u2100 that expand to 'a/c'
513518
# IDNA uses NFKC equivalence, so normalize for this check
514519
import unicodedata
@@ -524,6 +529,16 @@ def _checknetloc(netloc):
524529
raise ValueError("netloc '" + netloc + "' contains invalid " +
525530
"characters under NFKC normalization")
526531

532+
def _checknetloc(netloc):
533+
if not netloc:
534+
return
535+
if not netloc.isascii():
536+
_checknetloc_nfkc(netloc)
537+
538+
_, port = _parse_hostinfo(netloc)
539+
if port is not None:
540+
_validate_port(port)
541+
527542
def _check_bracketed_netloc(netloc):
528543
# Note that this function must mirror the splitting
529544
# done in NetlocResultMixins._hostinfo().
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Move port validation logic to parsing time. Patch by Miguel Brito.

0 commit comments

Comments
 (0)