diff --git a/glanceclient/common/http.py b/glanceclient/common/http.py index a5fb1537..9ba806f1 100644 --- a/glanceclient/common/http.py +++ b/glanceclient/common/http.py @@ -66,7 +66,11 @@ def encode_headers(headers): for h, v in headers.items(): if v is not None: # if the item is token, do not quote '+' as well. - safe = '=+/' if h in TOKEN_HEADERS else '/' + # NOTE(imacdonn): urlparse.quote() is intended for quoting the + # path part of a URL, but headers like x-image-meta-location + # include an entire URL. We should avoid encoding the colon in + # this case (bug #1788942) + safe = '=+/' if h in TOKEN_HEADERS else '/:' if six.PY2: # incoming items may be unicode, so get them into something # the py2 version of urllib can handle before percent encoding diff --git a/glanceclient/tests/unit/test_http.py b/glanceclient/tests/unit/test_http.py index 6eee005c..2f72b9a4 100644 --- a/glanceclient/tests/unit/test_http.py +++ b/glanceclient/tests/unit/test_http.py @@ -216,7 +216,11 @@ class TestClient(testtools.TestCase): def test_headers_encoding(self): value = u'ni\xf1o' - headers = {"test": value, "none-val": None, "Name": "value"} + fake_location = b'http://web_server:80/images/fake.img' + headers = {"test": value, + "none-val": None, + "Name": "value", + "x-image-meta-location": fake_location} encoded = http.encode_headers(headers) # Bug #1766235: According to RFC 8187, headers must be # encoded as 7-bit ASCII, so expect to see only displayable @@ -225,6 +229,8 @@ class TestClient(testtools.TestCase): self.assertNotIn("none-val", encoded) self.assertNotIn(b"none-val", encoded) self.assertEqual(b"value", encoded[b"Name"]) + # Bug #1788942: Colons in URL should not get percent-encoded + self.assertEqual(fake_location, encoded[b"x-image-meta-location"]) @mock.patch('keystoneauth1.adapter.Adapter.request') def test_http_duplicate_content_type_headers(self, mock_ksarq):