Skip to content

Commit c0ae48b

Browse files
committed
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
1 parent 93b49c5 commit c0ae48b

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

test/functional/swift_test_client.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,91 @@ def listing_items(method):
111111
items = []
112112

113113

114+
def putrequest(self, method, url, skip_host=False, skip_accept_encoding=False):
115+
'''Send a request to the server.
116+
117+
This is mostly a regurgitation of CPython's HTTPConnection.putrequest,
118+
but fixed up so we can still send arbitrary bytes in the request line
119+
on py3. See also: https://bugs.python.org/issue36274
120+
121+
To use, swap out a HTTP(S)Connection's putrequest with something like::
122+
123+
conn.putrequest = putrequest.__get__(conn)
124+
125+
:param method: specifies an HTTP request method, e.g. 'GET'.
126+
:param url: specifies the object being requested, e.g. '/index.html'.
127+
:param skip_host: if True does not add automatically a 'Host:' header
128+
:param skip_accept_encoding: if True does not add automatically an
129+
'Accept-Encoding:' header
130+
'''
131+
# (Mostly) inline the HTTPConnection implementation; just fix it
132+
# so we can send non-ascii request lines. For comparison, see
133+
# https://github.com/python/cpython/blob/v2.7.16/Lib/httplib.py#L888-L1003
134+
# and https://github.com/python/cpython/blob/v3.7.2/
135+
# Lib/http/client.py#L1061-L1183
136+
if self._HTTPConnection__response \
137+
and self._HTTPConnection__response.isclosed():
138+
self._HTTPConnection__response = None
139+
140+
if self._HTTPConnection__state == http_client._CS_IDLE:
141+
self._HTTPConnection__state = http_client._CS_REQ_STARTED
142+
else:
143+
raise http_client.CannotSendRequest(self._HTTPConnection__state)
144+
145+
self._method = method
146+
if not url:
147+
url = '/'
148+
self._path = url
149+
request = '%s %s %s' % (method, url, self._http_vsn_str)
150+
if not isinstance(request, bytes):
151+
# This choice of encoding is the whole reason we copy/paste from
152+
# cpython. When making backend requests, it should never be
153+
# necessary; however, we have some functional tests that want
154+
# to send non-ascii bytes.
155+
# TODO: when https://bugs.python.org/issue36274 is resolved, make
156+
# sure we fix up our API to match whatever upstream chooses to do
157+
self._output(request.encode('latin1'))
158+
else:
159+
self._output(request)
160+
161+
if self._http_vsn == 11:
162+
if not skip_host:
163+
netloc = ''
164+
if url.startswith('http'):
165+
nil, netloc, nil, nil, nil = urllib.parse.urlsplit(url)
166+
167+
if netloc:
168+
try:
169+
netloc_enc = netloc.encode("ascii")
170+
except UnicodeEncodeError:
171+
netloc_enc = netloc.encode("idna")
172+
self.putheader('Host', netloc_enc)
173+
else:
174+
if self._tunnel_host:
175+
host = self._tunnel_host
176+
port = self._tunnel_port
177+
else:
178+
host = self.host
179+
port = self.port
180+
181+
try:
182+
host_enc = host.encode("ascii")
183+
except UnicodeEncodeError:
184+
host_enc = host.encode("idna")
185+
186+
if host.find(':') >= 0:
187+
host_enc = b'[' + host_enc + b']'
188+
189+
if port == self.default_port:
190+
self.putheader('Host', host_enc)
191+
else:
192+
host_enc = host_enc.decode("ascii")
193+
self.putheader('Host', "%s:%s" % (host_enc, port))
194+
195+
if not skip_accept_encoding:
196+
self.putheader('Accept-Encoding', 'identity')
197+
198+
114199
class Connection(object):
115200
def __init__(self, config):
116201
for key in 'auth_host auth_port auth_ssl username password'.split():
@@ -132,6 +217,7 @@ def __init__(self, config):
132217
self.storage_netloc = None
133218
self.storage_path = None
134219
self.conn_class = None
220+
self.connection = None # until you call .http_connect()
135221

136222
@property
137223
def storage_url(self):
@@ -235,6 +321,7 @@ def http_connect(self):
235321
context=ssl._create_unverified_context())
236322
else:
237323
self.connection = self.conn_class(self.storage_netloc)
324+
self.connection.putrequest = putrequest.__get__(self.connection)
238325

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

0 commit comments

Comments
 (0)