Skip to content
Permalink
Browse files

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

Note that this only affects the functest client.

See also: https://bugs.python.org/issue36274

Change-Id: I1359c475fbe93db2f0fcc4b450be6dd5073f922e
  • Loading branch information...
tipabu committed Mar 12, 2019
1 parent 93b49c5 commit c0ae48ba9aafb0b91869ea3bae8da07a32088777
Showing with 87 additions and 0 deletions.
  1. +87 −0 test/functional/swift_test_client.py
@@ -111,6 +111,91 @@ def listing_items(method):
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):
def __init__(self, config):
for key in 'auth_host auth_port auth_ssl username password'.split():
@@ -132,6 +217,7 @@ def __init__(self, config):
self.storage_netloc = None
self.storage_path = None
self.conn_class = None
self.connection = None # until you call .http_connect()

@property
def storage_url(self):
@@ -235,6 +321,7 @@ def http_connect(self):
context=ssl._create_unverified_context())
else:
self.connection = self.conn_class(self.storage_netloc)
self.connection.putrequest = putrequest.__get__(self.connection)

def make_path(self, path=None, cfg=None):
if path is None:

0 comments on commit c0ae48b

Please sign in to comment.
You can’t perform that action at this time.