Don't quote colon in HTTP headers

Since the introduction of quoting of header content in
https://review.openstack.org/568698, the 'x-image-meta-location' header
has been broken, because urllib.quote() is really intended to be applied
to only the path section of a URL, but in this case, it gets applied to
the entire URL, and catches the colon that separates the scheme from the
remainder of the URL.

This change adds the colon to the list of characters that should not get
quoted. Since a colon can be directly represented in ASCII, this should
not invalidate the previous change.

Change-Id: I76a1c9a361b6c9f6eb95ae766b8c3bcf2267703a
Closes-Bug: #1788942
(cherry picked from commit 1156346dc2)
This commit is contained in:
imacdonn 2018-11-01 21:36:11 +00:00 committed by iain MacDonnell
parent 395d0f157b
commit e0673a17fd
2 changed files with 12 additions and 2 deletions

View File

@ -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

View File

@ -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):