else:
raise CannotSendRequest()
- # Save the method we use, we need it later in the response phase
+ # Save the method for use later in the response phase
self._method = method
- if not url:
- url = '/'
- # Prevent CVE-2019-9740.
- match = _contains_disallowed_url_pchar_re.search(url)
- if match:
- raise InvalidURL("URL can't contain control characters. %r "
- "(found at least %r)"
- % (url, match.group()))
- hdr = '%s %s %s' % (method, url, self._http_vsn_str)
- self._output(hdr)
+ url = url or '/'
+ self._validate_path(url)
+
+ request = '%s %s %s' % (method, url, self._http_vsn_str)
+
+ self._output(self._encode_request(request))
if self._http_vsn == 11:
# Issue some standard headers for better HTTP/1.1 compliance
# For HTTP/1.0, the server will assume "not chunked"
pass
+ def _encode_request(self, request):
+ # On Python 2, request is already encoded (default)
+ return request
+
+ def _validate_path(self, url):
+ """Validate a url for putrequest."""
+ # Prevent CVE-2019-9740.
+ match = _contains_disallowed_url_pchar_re.search(url)
+ if match:
+ msg = (
+ "URL can't contain control characters. {url!r} "
+ "(found at least {matched!r})"
+ ).format(matched=match.group(), url=url)
+ raise InvalidURL(msg)
+
def putheader(self, header, *values):
"""Send a request header line to the server.
with self.assertRaisesRegexp(socket.error, "Invalid response"):
conn._tunnel()
+ def test_putrequest_override_validation(self):
+ """
+ It should be possible to override the default validation
+ behavior in putrequest (bpo-38216).
+ """
+ class UnsafeHTTPConnection(httplib.HTTPConnection):
+ def _validate_path(self, url):
+ pass
+
+ conn = UnsafeHTTPConnection('example.com')
+ conn.sock = FakeSocket('')
+ conn.putrequest('GET', '/\x00')
+
+
class OfflineTest(TestCase):
def test_responses(self):
self.assertEqual(httplib.responses[httplib.NOT_FOUND], "Not Found")