Skip to content
This repository was archived by the owner on Jan 18, 2025. It is now read-only.

Commit f41eb74

Browse files
committed
Switched headers to be normalized as strings, not bytes, in keeping with httplib2.
1 parent 3c02a81 commit f41eb74

4 files changed

Lines changed: 51 additions & 16 deletions

File tree

oauth2client/client.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,9 +397,32 @@ def delete(self):
397397
finally:
398398
self.release_lock()
399399

400+
# Since strings ARE unicode in Python3, "cleaning" the string doesn't mean
401+
# just converting to str.
402+
#
403+
# As per the httplib2 documentation:
404+
#
405+
# ** THE RESPONSE HEADERS ARE STRINGS, BUT THE CONTENT BODY IS BYTES **
406+
#
407+
def _clean_header(s):
408+
"""Always returns something of type str. Raise UnicodeEncodeError if
409+
unconvertable to ascii."""
410+
411+
if not isinstance(s, str):
412+
# str(b'foo') will return "b'foo'" in Py3, not what we want.
413+
if isinstance(s, bytes):
414+
# Binary string in Py3
415+
s = s.decode('utf-8')
416+
else:
417+
s = str(s)
418+
419+
# We're trying to generate the UnicodeEncodeError here if the string is
420+
# unconvertable, AND keep this a string in both Py2 and Py3.
421+
s = str((s.encode('ascii')).decode('utf-8'))
422+
return s
400423

401424
def clean_headers(headers):
402-
"""Forces header keys and values to be strings, i.e not unicode.
425+
"""Forces header keys and values to be strings suitable for httplib2.
403426
404427
The httplib module just concats the header keys and values in a way that may
405428
make the message header a unicode string, which, if it then tries to
@@ -414,8 +437,8 @@ def clean_headers(headers):
414437
clean = {}
415438
try:
416439
for k, v in six.iteritems(headers):
417-
clean_k = k if isinstance(k, bytes) else str(k).encode('ascii')
418-
clean_v = v if isinstance(v, bytes) else str(v).encode('ascii')
440+
clean_k = _clean_header(k)
441+
clean_v = _clean_header(v)
419442
clean[clean_k] = clean_v
420443
except UnicodeEncodeError:
421444
raise NonAsciiHeaderError(k + ': ' + v)

tests/test_appengine.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@
2929
import time
3030
import unittest
3131
import urllib
32-
import urlparse
32+
33+
try:
34+
import urlparse
35+
except ImportError:
36+
from urllib.parse import urlparse
3337

3438
import dev_appserver
3539
dev_appserver.fix_sys_path()

tests/test_jwt.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def test_credentials_good(self):
220220
])
221221
http = credentials.authorize(http)
222222
resp, content = http.request('http://example.org')
223-
self.assertEqual(b'Bearer 1/3w', content[b'Authorization'])
223+
self.assertEqual('Bearer 1/3w', content['Authorization'])
224224

225225
def test_credentials_to_from_json(self):
226226
private_key = datafile('privatekey.%s' % self.format)
@@ -257,7 +257,7 @@ def test_credentials_refresh_without_storage(self):
257257

258258
content = self._credentials_refresh(credentials)
259259

260-
self.assertEqual(b'Bearer 3/3w', content[b'Authorization'])
260+
self.assertEqual('Bearer 3/3w', content['Authorization'])
261261

262262
def test_credentials_refresh_with_storage(self):
263263
private_key = datafile('privatekey.%s' % self.format)
@@ -275,7 +275,7 @@ def test_credentials_refresh_with_storage(self):
275275

276276
content = self._credentials_refresh(credentials)
277277

278-
self.assertEqual(b'Bearer 3/3w', content[b'Authorization'])
278+
self.assertEqual('Bearer 3/3w', content['Authorization'])
279279
os.unlink(filename)
280280

281281

tests/test_oauth2client.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
Unit tests for oauth2client.
2121
"""
2222

23+
# pylint: disable=bad-indentation
2324
__author__ = '[email protected] (Joe Gregorio)'
2425

2526
import base64
@@ -531,7 +532,7 @@ def test_token_refresh_success(self):
531532
])
532533
http = self.credentials.authorize(http)
533534
resp, content = http.request('http://example.com')
534-
self.assertEqual(b'Bearer 1/3w', content[b'Authorization'])
535+
self.assertEqual('Bearer 1/3w', content['Authorization'])
535536
self.assertFalse(self.credentials.access_token_expired)
536537
self.assertEqual(token_response, self.credentials.token_response)
537538

@@ -599,17 +600,17 @@ def test_unicode_header_checks(self):
599600

600601
# First, test that we correctly encode basic objects, making sure
601602
# to include a bytes object. Note that oauth2client will normalize
602-
# everything to bytes, no matter what python version we're in.
603+
# everything to strings, no matter what python version we're in.
603604
http = credentials.authorize(HttpMock(headers={'status': '200'}))
604605
headers = {u'foo': 3, b'bar': True, 'baz': b'abc'}
605-
cleaned_headers = {b'foo': b'3', b'bar': b'True', b'baz': b'abc'}
606+
cleaned_headers = {'foo': '3', 'bar': 'True', 'baz': 'abc'}
606607
http.request(u'http://example.com', method=u'GET', headers=headers)
607608
for k, v in cleaned_headers.items():
608609
self.assertTrue(k in http.headers)
609610
self.assertEqual(v, http.headers[k])
610611

611612
# Next, test that we do fail on unicode.
612-
unicode_str = six.unichr(40960) + 'abcd'
613+
unicode_str = u'\u2602' + 'abcd'
613614
self.assertRaises(
614615
NonAsciiHeaderError,
615616
http.request,
@@ -631,14 +632,21 @@ def test_no_unicode_in_request_params(self):
631632
http = HttpMock(headers={'status': '200'})
632633
http = credentials.authorize(http)
633634
http.request(u'http://example.com', method=u'GET', headers={u'foo': u'bar'})
635+
636+
# oauth2client uses httplib2 and httplib2 says:
637+
# "** THE RESPONSE HEADERS ARE STRINGS, BUT THE CONTENT BODY IS BYTES **"
638+
# and from https://github.com/jcgregorio/httplib2/wiki/Examples-Python3
639+
# "In httplib2, the response headers are strings..."
640+
#
641+
# So, the headers should be of type str.
634642
for k, v in six.iteritems(http.headers):
635-
self.assertEqual(six.binary_type, type(k))
636-
self.assertEqual(six.binary_type, type(v))
643+
self.assertTrue(isinstance(k, str))
644+
self.assertTrue(isinstance(v, str))
637645

638646
# Test again with unicode strings that can't simply be converted to ASCII.
639647
try:
640648
http.request(
641-
u'http://example.com', method=u'GET', headers={u'foo': u'\N{COMET}'})
649+
u'http://example.com', method=u'GET', headers={u'foo': u'\u2602'})
642650
self.fail('Expected exception to be raised.')
643651
except NonAsciiHeaderError:
644652
pass
@@ -724,7 +732,7 @@ def test_auth_header_sent(self):
724732
])
725733
http = self.credentials.authorize(http)
726734
resp, content = http.request('http://example.com')
727-
self.assertEqual(b'Bearer foo', content[b'Authorization'])
735+
self.assertEqual('Bearer foo', content['Authorization'])
728736

729737

730738
class TestAssertionCredentials(unittest.TestCase):
@@ -755,7 +763,7 @@ def test_assertion_refresh(self):
755763
])
756764
http = self.credentials.authorize(http)
757765
resp, content = http.request('http://example.com')
758-
self.assertEqual(b'Bearer 1/3w', content[b'Authorization'])
766+
self.assertEqual('Bearer 1/3w', content['Authorization'])
759767

760768
def test_token_revoke_success(self):
761769
_token_revoke_test_helper(

0 commit comments

Comments
 (0)