Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0b39a55

Browse files
committed
Issue #14132, Issue #17214: Merge two redirect handling fixes from 3.5
2 parents c944c2d + e6f0609 commit 0b39a55

4 files changed

Lines changed: 76 additions & 5 deletions

File tree

Lib/test/test_urllib.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Regresssion tests for urllib"""
1+
"""Regresssion tests for what was in Python 2's "urllib" module"""
22

33
import urllib.parse
44
import urllib.request
@@ -86,10 +86,11 @@ class FakeHTTPConnection(http.client.HTTPConnection):
8686

8787
# buffer to store data for verification in urlopen tests.
8888
buf = None
89-
fakesock = FakeSocket(fakedata)
9089

9190
def connect(self):
92-
self.sock = self.fakesock
91+
self.sock = FakeSocket(self.fakedata)
92+
type(self).fakesock = self.sock
93+
FakeHTTPConnection.fakedata = fakedata
9394

9495
return FakeHTTPConnection
9596

Lib/test/test_urllib2.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,57 @@ def test_redirect_fragment(self):
12081208
fp = o.open('http://www.example.com')
12091209
self.assertEqual(fp.geturl(), redirected_url.strip())
12101210

1211+
def test_redirect_no_path(self):
1212+
# Issue 14132: Relative redirect strips original path
1213+
real_class = http.client.HTTPConnection
1214+
response1 = b"HTTP/1.1 302 Found\r\nLocation: ?query\r\n\r\n"
1215+
http.client.HTTPConnection = test_urllib.fakehttp(response1)
1216+
self.addCleanup(setattr, http.client, "HTTPConnection", real_class)
1217+
urls = iter(("/path", "/path?query"))
1218+
def request(conn, method, url, *pos, **kw):
1219+
self.assertEqual(url, next(urls))
1220+
real_class.request(conn, method, url, *pos, **kw)
1221+
# Change response for subsequent connection
1222+
conn.__class__.fakedata = b"HTTP/1.1 200 OK\r\n\r\nHello!"
1223+
http.client.HTTPConnection.request = request
1224+
fp = urllib.request.urlopen("http://python.org/path")
1225+
self.assertEqual(fp.geturl(), "http://python.org/path?query")
1226+
1227+
def test_redirect_encoding(self):
1228+
# Some characters in the redirect target may need special handling,
1229+
# but most ASCII characters should be treated as already encoded
1230+
class Handler(urllib.request.HTTPHandler):
1231+
def http_open(self, req):
1232+
result = self.do_open(self.connection, req)
1233+
self.last_buf = self.connection.buf
1234+
# Set up a normal response for the next request
1235+
self.connection = test_urllib.fakehttp(
1236+
b'HTTP/1.1 200 OK\r\n'
1237+
b'Content-Length: 3\r\n'
1238+
b'\r\n'
1239+
b'123'
1240+
)
1241+
return result
1242+
handler = Handler()
1243+
opener = urllib.request.build_opener(handler)
1244+
tests = (
1245+
(b'/p\xC3\xA5-dansk/', b'/p%C3%A5-dansk/'),
1246+
(b'/spaced%20path/', b'/spaced%20path/'),
1247+
(b'/spaced path/', b'/spaced%20path/'),
1248+
(b'/?p\xC3\xA5-dansk', b'/?p%C3%A5-dansk'),
1249+
)
1250+
for [location, result] in tests:
1251+
with self.subTest(repr(location)):
1252+
handler.connection = test_urllib.fakehttp(
1253+
b'HTTP/1.1 302 Redirect\r\n'
1254+
b'Location: ' + location + b'\r\n'
1255+
b'\r\n'
1256+
)
1257+
response = opener.open('http://example.com/')
1258+
expected = b'GET ' + result + b' '
1259+
request = handler.last_buf
1260+
self.assertTrue(request.startswith(expected), repr(request))
1261+
12111262
def test_proxy(self):
12121263
o = OpenerDirector()
12131264
ph = urllib.request.ProxyHandler(dict(http="proxy.example.com:3128"))

Lib/urllib/request.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
import posixpath
9292
import re
9393
import socket
94+
import string
9495
import sys
9596
import time
9697
import collections
@@ -676,8 +677,12 @@ def redirect_request(self, req, fp, code, msg, headers, newurl):
676677
# from the user (of urllib.request, in this case). In practice,
677678
# essentially all clients do redirect in this case, so we do
678679
# the same.
679-
# be conciliant with URIs containing a space
680+
681+
# Be conciliant with URIs containing a space. This is mainly
682+
# redundant with the more complete encoding done in http_error_302(),
683+
# but it is kept for compatibility with other callers.
680684
newurl = newurl.replace(' ', '%20')
685+
681686
CONTENT_HEADERS = ("content-length", "content-type")
682687
newheaders = dict((k, v) for k, v in req.headers.items()
683688
if k.lower() not in CONTENT_HEADERS)
@@ -712,11 +717,16 @@ def http_error_302(self, req, fp, code, msg, headers):
712717
"%s - Redirection to url '%s' is not allowed" % (msg, newurl),
713718
headers, fp)
714719

715-
if not urlparts.path:
720+
if not urlparts.path and urlparts.netloc:
716721
urlparts = list(urlparts)
717722
urlparts[2] = "/"
718723
newurl = urlunparse(urlparts)
719724

725+
# http.client.parse_headers() decodes as ISO-8859-1. Recover the
726+
# original bytes and percent-encode non-ASCII bytes, and any special
727+
# characters such as the space.
728+
newurl = quote(
729+
newurl, encoding="iso-8859-1", safe=string.punctuation)
720730
newurl = urljoin(req.full_url, newurl)
721731

722732
# XXX Probably want to forget about the state of the current

Misc/NEWS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,15 @@ Core and Builtins
277277
Library
278278
-------
279279

280+
- Issue #14132: Fix urllib.request redirect handling when the target only has
281+
a query string. Original fix by Ján Janech.
282+
283+
- Issue #17214: The "urllib.request" module now percent-encodes non-ASCII
284+
bytes found in redirect target URLs. Some servers send Location header
285+
fields with non-ASCII bytes, but "http.client" requires the request target
286+
to be ASCII-encodable, otherwise a UnicodeEncodeError is raised. Based on
287+
patch by Christian Heimes.
288+
280289
- Issue #27033: The default value of the decode_data parameter for
281290
smtpd.SMTPChannel and smtpd.SMTPServer constructors is changed to False.
282291

0 commit comments

Comments
 (0)