Merge "py3: Stop using stdlib's putrequest(); it only does ASCII"

This commit is contained in:
Zuul 2019-05-04 00:13:42 +00:00 committed by Gerrit Code Review
commit 1c899f78d4

View File

@ -111,6 +111,91 @@ def listing_items(method):
items = [] items = []
def putrequest(self, method, url, skip_host=False, skip_accept_encoding=False):
'''Send a request to the server.
This is mostly a regurgitation of CPython's HTTPConnection.putrequest,
but fixed up so we can still send arbitrary bytes in the request line
on py3. See also: https://bugs.python.org/issue36274
To use, swap out a HTTP(S)Connection's putrequest with something like::
conn.putrequest = putrequest.__get__(conn)
:param method: specifies an HTTP request method, e.g. 'GET'.
:param url: specifies the object being requested, e.g. '/index.html'.
:param skip_host: if True does not add automatically a 'Host:' header
:param skip_accept_encoding: if True does not add automatically an
'Accept-Encoding:' header
'''
# (Mostly) inline the HTTPConnection implementation; just fix it
# so we can send non-ascii request lines. For comparison, see
# https://github.com/python/cpython/blob/v2.7.16/Lib/httplib.py#L888-L1003
# and https://github.com/python/cpython/blob/v3.7.2/
# Lib/http/client.py#L1061-L1183
if self._HTTPConnection__response \
and self._HTTPConnection__response.isclosed():
self._HTTPConnection__response = None
if self._HTTPConnection__state == http_client._CS_IDLE:
self._HTTPConnection__state = http_client._CS_REQ_STARTED
else:
raise http_client.CannotSendRequest(self._HTTPConnection__state)
self._method = method
if not url:
url = '/'
self._path = url
request = '%s %s %s' % (method, url, self._http_vsn_str)
if not isinstance(request, bytes):
# This choice of encoding is the whole reason we copy/paste from
# cpython. When making backend requests, it should never be
# necessary; however, we have some functional tests that want
# to send non-ascii bytes.
# TODO: when https://bugs.python.org/issue36274 is resolved, make
# sure we fix up our API to match whatever upstream chooses to do
self._output(request.encode('latin1'))
else:
self._output(request)
if self._http_vsn == 11:
if not skip_host:
netloc = ''
if url.startswith('http'):
nil, netloc, nil, nil, nil = urllib.parse.urlsplit(url)
if netloc:
try:
netloc_enc = netloc.encode("ascii")
except UnicodeEncodeError:
netloc_enc = netloc.encode("idna")
self.putheader('Host', netloc_enc)
else:
if self._tunnel_host:
host = self._tunnel_host
port = self._tunnel_port
else:
host = self.host
port = self.port
try:
host_enc = host.encode("ascii")
except UnicodeEncodeError:
host_enc = host.encode("idna")
if host.find(':') >= 0:
host_enc = b'[' + host_enc + b']'
if port == self.default_port:
self.putheader('Host', host_enc)
else:
host_enc = host_enc.decode("ascii")
self.putheader('Host', "%s:%s" % (host_enc, port))
if not skip_accept_encoding:
self.putheader('Accept-Encoding', 'identity')
class Connection(object): class Connection(object):
def __init__(self, config): def __init__(self, config):
for key in 'auth_host auth_port auth_ssl username password'.split(): for key in 'auth_host auth_port auth_ssl username password'.split():
@ -132,6 +217,7 @@ class Connection(object):
self.storage_netloc = None self.storage_netloc = None
self.storage_path = None self.storage_path = None
self.conn_class = None self.conn_class = None
self.connection = None # until you call .http_connect()
@property @property
def storage_url(self): def storage_url(self):
@ -235,6 +321,7 @@ class Connection(object):
context=ssl._create_unverified_context()) context=ssl._create_unverified_context())
else: else:
self.connection = self.conn_class(self.storage_netloc) self.connection = self.conn_class(self.storage_netloc)
self.connection.putrequest = putrequest.__get__(self.connection)
def make_path(self, path=None, cfg=None): def make_path(self, path=None, cfg=None):
if path is None: if path is None: