Skip to content

Commit 9ec4f32

Browse files
enforcing encodings on XMLRPC calls so we can safely use unicode characters
1 parent f6a8d6c commit 9ec4f32

2 files changed

Lines changed: 69 additions & 16 deletions

File tree

SoftLayer/transports.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ def __call__(self, request):
205205
request.url = '/'.join([self.endpoint_url, request.service])
206206
request.payload = xmlrpc.client.dumps(tuple(largs),
207207
methodname=request.method,
208-
allow_none=True)
208+
allow_none=True,
209+
encoding="iso-8859-1")
209210

210211
# Prefer the request setting, if it's not None
211212
verify = request.verify
@@ -214,7 +215,7 @@ def __call__(self, request):
214215

215216
try:
216217
resp = self.client.request('POST', request.url,
217-
data=request.payload,
218+
data=request.payload.encode(),
218219
auth=auth,
219220
headers=request.transport_headers,
220221
timeout=self.timeout,
@@ -273,7 +274,7 @@ def print_reproduceable(self, request):
273274
#auth=HTTPBasicAuth('apikey', YOUR_CLOUD_API_KEY)
274275
auth=None
275276
url = '$url'
276-
payload = """$payload"""
277+
payload = $payload
277278
transport_headers = $transport_headers
278279
timeout = $timeout
279280
verify = $verify
@@ -287,6 +288,7 @@ def print_reproduceable(self, request):
287288

288289
safe_payload = re.sub(r'<string>[a-z0-9]{64}</string>', r'<string>API_KEY_GOES_HERE</string>', request.payload)
289290
safe_payload = re.sub(r'(\s+)', r' ', safe_payload)
291+
safe_payload = safe_payload.encode()
290292
substitutions = dict(url=request.url, payload=safe_payload, transport_headers=request.transport_headers,
291293
timeout=self.timeout, verify=request.verify, cert=request.cert,
292294
proxy=_proxies_dict(self.proxy))

tests/transport_tests.py

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def set_up(self):
4848
def test_call(self, request):
4949
request.return_value = self.response
5050

51-
data = '''<?xml version='1.0'?>
51+
data = '''<?xml version='1.0' encoding='iso-8859-1'?>
5252
<methodCall>
5353
<methodName>getObject</methodName>
5454
<params>
@@ -63,7 +63,7 @@ def test_call(self, request):
6363
</param>
6464
</params>
6565
</methodCall>
66-
'''
66+
'''.encode()
6767

6868
req = transports.Request()
6969
req.service = 'SoftLayer_Service'
@@ -134,7 +134,7 @@ def test_identifier(self, request):
134134
"""<member>
135135
<name>id</name>
136136
<value><int>1234</int></value>
137-
</member>""", kwargs['data'])
137+
</member>""".encode(), kwargs['data'])
138138

139139
@mock.patch('SoftLayer.transports.requests.Session.request')
140140
def test_filter(self, request):
@@ -152,7 +152,7 @@ def test_filter(self, request):
152152
"""<member>
153153
<name>operation</name>
154154
<value><string>^= prefix</string></value>
155-
</member>""", kwargs['data'])
155+
</member>""".encode(), kwargs['data'])
156156

157157
@mock.patch('SoftLayer.transports.requests.Session.request')
158158
def test_limit_offset(self, request):
@@ -169,10 +169,10 @@ def test_limit_offset(self, request):
169169
self.assertIn("""<member>
170170
<name>resultLimit</name>
171171
<value><struct>
172-
<member>""", kwargs['data'])
172+
<member>""".encode(), kwargs['data'])
173173
self.assertIn("""<name>limit</name>
174174
<value><int>10</int></value>
175-
</member>""", kwargs['data'])
175+
</member>""".encode(), kwargs['data'])
176176

177177
@mock.patch('SoftLayer.transports.requests.Session.request')
178178
def test_old_mask(self, request):
@@ -194,7 +194,7 @@ def test_old_mask(self, request):
194194
<value><string>nested</string></value>
195195
</member>
196196
</struct></value>
197-
</member>""", kwargs['data'])
197+
</member>""".encode(), kwargs['data'])
198198

199199
@mock.patch('SoftLayer.transports.requests.Session.request')
200200
def test_mask_call_no_mask_prefix(self, request):
@@ -209,7 +209,7 @@ def test_mask_call_no_mask_prefix(self, request):
209209

210210
args, kwargs = request.call_args
211211
self.assertIn(
212-
"<value><string>mask[something.nested]</string></value>",
212+
"<value><string>mask[something.nested]</string></value>".encode(),
213213
kwargs['data'])
214214

215215
@mock.patch('SoftLayer.transports.requests.Session.request')
@@ -225,7 +225,7 @@ def test_mask_call_v2(self, request):
225225

226226
args, kwargs = request.call_args
227227
self.assertIn(
228-
"<value><string>mask[something[nested]]</string></value>",
228+
"<value><string>mask[something[nested]]</string></value>".encode(),
229229
kwargs['data'])
230230

231231
@mock.patch('SoftLayer.transports.requests.Session.request')
@@ -241,7 +241,7 @@ def test_mask_call_filteredMask(self, request):
241241

242242
args, kwargs = request.call_args
243243
self.assertIn(
244-
"<value><string>filteredMask[something[nested]]</string></value>",
244+
"<value><string>filteredMask[something[nested]]</string></value>".encode(),
245245
kwargs['data'])
246246

247247
@mock.patch('SoftLayer.transports.requests.Session.request')
@@ -256,7 +256,7 @@ def test_mask_call_v2_dot(self, request):
256256
self.transport(req)
257257

258258
args, kwargs = request.call_args
259-
self.assertIn("<value><string>mask.something.nested</string></value>",
259+
self.assertIn("<value><string>mask.something.nested</string></value>".encode(),
260260
kwargs['data'])
261261

262262
@mock.patch('SoftLayer.transports.requests.Session.request')
@@ -287,7 +287,7 @@ def test_print_reproduceable(self):
287287
def test_ibm_id_call(self, auth, request):
288288
request.return_value = self.response
289289

290-
data = '''<?xml version='1.0'?>
290+
data = '''<?xml version='1.0' encoding='iso-8859-1'?>
291291
<methodCall>
292292
<methodName>getObject</methodName>
293293
<params>
@@ -302,7 +302,7 @@ def test_ibm_id_call(self, auth, request):
302302
</param>
303303
</params>
304304
</methodCall>
305-
'''
305+
'''.encode()
306306

307307
req = transports.Request()
308308
req.service = 'SoftLayer_Service'
@@ -360,6 +360,57 @@ def test_call_large_number_response(self, request):
360360
resp = self.transport(req)
361361
self.assertEqual(resp[0]['bytesUsed'], 2666148982056)
362362

363+
@mock.patch('SoftLayer.transports.requests.Session.request')
364+
def test_nonascii_characters(self, request):
365+
request.return_value = self.response
366+
hostname = 'testé'
367+
data = '''<?xml version='1.0' encoding='iso-8859-1'?>
368+
<methodCall>
369+
<methodName>getObject</methodName>
370+
<params>
371+
<param>
372+
<value><struct>
373+
<member>
374+
<name>headers</name>
375+
<value><struct>
376+
</struct></value>
377+
</member>
378+
</struct></value>
379+
</param>
380+
<param>
381+
<value><struct>
382+
<member>
383+
<name>hostname</name>
384+
<value><string>testé</string></value>
385+
</member>
386+
</struct></value>
387+
</param>
388+
</params>
389+
</methodCall>
390+
'''.encode()
391+
392+
req = transports.Request()
393+
req.service = 'SoftLayer_Service'
394+
req.method = 'getObject'
395+
req.args = ({'hostname': hostname},)
396+
req.transport_user = "testUser"
397+
req.transport_password = "testApiKey"
398+
resp = self.transport(req)
399+
400+
request.assert_called_with('POST',
401+
'http://something9999999999999999999999.com/SoftLayer_Service',
402+
headers={'Content-Type': 'application/xml',
403+
'User-Agent': consts.USER_AGENT},
404+
proxies=None,
405+
data=data,
406+
timeout=None,
407+
cert=None,
408+
verify=True,
409+
auth=mock.ANY)
410+
self.assertEqual(resp, [])
411+
self.assertIsInstance(resp, transports.SoftLayerListResult)
412+
self.assertEqual(resp.total_count, 10)
413+
363414

364415
@mock.patch('SoftLayer.transports.requests.Session.request')
365416
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)