Merge "py3: Stop using stdlib's putrequest(); it only does ASCII"
This commit is contained in:
commit
1c899f78d4
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user