diff --git a/keystoneauth1/session.py b/keystoneauth1/session.py index 6ab1e144..d9e99d32 100644 --- a/keystoneauth1/session.py +++ b/keystoneauth1/session.py @@ -79,6 +79,26 @@ def _mv_legacy_headers_for_service(mv_service_type): return headers +def _sanitize_headers(headers): + """Ensure headers are strings and not bytes.""" + str_dict = {} + for k, v in headers.items(): + if six.PY3: + # requests expects headers to be str type in python3, which means + # if we get a bytes we need to decode it into a str + k = k.decode('ASCII') if isinstance(k, six.binary_type) else k + if v is not None: + v = v.decode('ASCII') if isinstance(v, six.binary_type) else v + else: + # requests expects headers to be str type in python2, which means + # if we get a unicode we need to encode it to ASCII into a str + k = k.encode('ASCII') if isinstance(k, six.text_type) else k + if v is not None: + v = v.encode('ASCII') if isinstance(v, six.text_type) else v + str_dict[k] = v + return str_dict + + class _JSONEncoder(json.JSONEncoder): def default(self, o): @@ -711,6 +731,10 @@ class Session(object): for k, v in self.additional_headers.items(): headers.setdefault(k, v) + # Bug #1766235: some headers may be bytes + headers = _sanitize_headers(headers) + kwargs['headers'] = headers + kwargs.setdefault('verify', self.verify) if requests_auth: diff --git a/keystoneauth1/tests/unit/test_session.py b/keystoneauth1/tests/unit/test_session.py index 94cc7d15..a7165e17 100644 --- a/keystoneauth1/tests/unit/test_session.py +++ b/keystoneauth1/tests/unit/test_session.py @@ -17,6 +17,7 @@ import sys import uuid import mock +from oslo_utils import encodeutils import requests import requests.auth import six @@ -987,7 +988,12 @@ class SessionAuthTests(utils.TestCase): 'X-OpenStack-Request-ID': request_id, }) - resp = sess.get(self.TEST_URL) + resp = sess.get( + self.TEST_URL, + headers={ + encodeutils.safe_encode('x-bytes-header'): + encodeutils.safe_encode('bytes-value') + }) self.assertEqual(response, resp.json()) @@ -998,6 +1004,7 @@ class SessionAuthTests(utils.TestCase): self.assertIn('curl -g -i -X GET {url}'.format(url=self.TEST_URL), request_output) + self.assertIn('-H "x-bytes-header: bytes-value"', request_output) self.assertEqual('[200] Content-Type: application/json ' 'X-OpenStack-Request-ID: ' '{id}'.format(id=request_id), response_output) diff --git a/releasenotes/notes/bug-1766235wq-0de60d0f996c6bfb.yaml b/releasenotes/notes/bug-1766235wq-0de60d0f996c6bfb.yaml new file mode 100644 index 00000000..1c45aedc --- /dev/null +++ b/releasenotes/notes/bug-1766235wq-0de60d0f996c6bfb.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + [`bug 1766235 `_] + Fixed an issue where passing headers in as bytes rather than strings + would cause a sorting issue.