Skip to content

Commit 5b18ce6

Browse files
jaraconed-deily
authored andcommitted
[3.6] bpo-38216, bpo-36274: Allow subclasses to separately override validation and encoding behavior (GH-16448) (GH-16462)
(cherry picked from commit 7774d78) Co-authored-by: Jason R. Coombs <[email protected]>
1 parent 1698cac commit 5b18ce6

File tree

3 files changed

+51
-10
lines changed

3 files changed

+51
-10
lines changed

Lib/http/client.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,19 +1117,15 @@ def putrequest(self, method, url, skip_host=False,
11171117
else:
11181118
raise CannotSendRequest(self.__state)
11191119

1120-
# Save the method we use, we need it later in the response phase
1120+
# Save the method for use later in the response phase
11211121
self._method = method
1122-
if not url:
1123-
url = '/'
1124-
# Prevent CVE-2019-9740.
1125-
match = _contains_disallowed_url_pchar_re.search(url)
1126-
if match:
1127-
raise InvalidURL(f"URL can't contain control characters. {url!r} "
1128-
f"(found at least {match.group()!r})")
1122+
1123+
url = url or '/'
1124+
self._validate_path(url)
1125+
11291126
request = '%s %s %s' % (method, url, self._http_vsn_str)
11301127

1131-
# Non-ASCII characters should have been eliminated earlier
1132-
self._output(request.encode('ascii'))
1128+
self._output(self._encode_request(request))
11331129

11341130
if self._http_vsn == 11:
11351131
# Issue some standard headers for better HTTP/1.1 compliance
@@ -1207,6 +1203,18 @@ def putrequest(self, method, url, skip_host=False,
12071203
# For HTTP/1.0, the server will assume "not chunked"
12081204
pass
12091205

1206+
def _encode_request(self, request):
1207+
# ASCII also helps prevent CVE-2019-9740.
1208+
return request.encode('ascii')
1209+
1210+
def _validate_path(self, url):
1211+
"""Validate a url for putrequest."""
1212+
# Prevent CVE-2019-9740.
1213+
match = _contains_disallowed_url_pchar_re.search(url)
1214+
if match:
1215+
raise InvalidURL(f"URL can't contain control characters. {url!r} "
1216+
f"(found at least {match.group()!r})")
1217+
12101218
def putheader(self, header, *values):
12111219
"""Send a request header line to the server.
12121220

Lib/test/test_httplib.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,34 @@ def run_server():
11321132
thread.join()
11331133
self.assertEqual(result, b"proxied data\n")
11341134

1135+
def test_putrequest_override_validation(self):
1136+
"""
1137+
It should be possible to override the default validation
1138+
behavior in putrequest (bpo-38216).
1139+
"""
1140+
class UnsafeHTTPConnection(client.HTTPConnection):
1141+
def _validate_path(self, url):
1142+
pass
1143+
1144+
conn = UnsafeHTTPConnection('example.com')
1145+
conn.sock = FakeSocket('')
1146+
conn.putrequest('GET', '/\x00')
1147+
1148+
def test_putrequest_override_encoding(self):
1149+
"""
1150+
It should be possible to override the default encoding
1151+
to transmit bytes in another encoding even if invalid
1152+
(bpo-36274).
1153+
"""
1154+
class UnsafeHTTPConnection(client.HTTPConnection):
1155+
def _encode_request(self, str_url):
1156+
return str_url.encode('utf-8')
1157+
1158+
conn = UnsafeHTTPConnection('example.com')
1159+
conn.sock = FakeSocket('')
1160+
conn.putrequest('GET', '/☃')
1161+
1162+
11351163
class ExtendedReadTest(TestCase):
11361164
"""
11371165
Test peek(), read1(), readline()
@@ -1256,6 +1284,7 @@ def test_peek_0(self):
12561284
p = self.resp.peek(0)
12571285
self.assertLessEqual(0, len(p))
12581286

1287+
12591288
class ExtendedReadTestChunked(ExtendedReadTest):
12601289
"""
12611290
Test peek(), read1(), readline() in chunked mode
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Allow the rare code that wants to send invalid http requests from the
2+
`http.client` library a way to do so. The fixes for bpo-30458 led to
3+
breakage for some projects that were relying on this ability to test their
4+
own behavior in the face of bad requests.

0 commit comments

Comments
 (0)