From c00fca4a09942181937794d711643511e3b1e4a5 Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Fri, 29 May 2020 16:04:47 -0500 Subject: [PATCH] Make header Case Insensitive In case of global-request-id request, Adapter send two global request id header - "X-OpenStack-Request-ID" - "X-Openstack-Request-Id". Example: https://zuul.opendev.org/t/openstack/build/c5b1debf78df4aa3bdda34f0b4c53c37/log/testrepository.subunit#2385 This is becasue of the header not being Case Insensitive and end up with two different name of same header with difference of cap 'D'. Unit test for whether request global-request-id has precedence over adapter fail many times because of how different python version treat the dict. py3.6 and above are all good as dict maintain the insertion ordered but py3.5 can fail it any time. We can see consistent failure in py35 jobs: - https://review.opendev.org/#/c/730687/ Let's make the headers always Case Insensitive which is what RFC says. Change-Id: Iba707dd0506d22e144aca4fdfc9b140c8e37ae02 Closes-Bug: #1881351 --- keystoneauth1/adapter.py | 11 ++++++++++- keystoneauth1/session.py | 8 ++++++++ keystoneauth1/tests/unit/test_session.py | 4 ++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/keystoneauth1/adapter.py b/keystoneauth1/adapter.py index 8d438f37..c23d9a39 100644 --- a/keystoneauth1/adapter.py +++ b/keystoneauth1/adapter.py @@ -13,6 +13,8 @@ import os import warnings +import requests + from keystoneauth1 import _fair_semaphore from keystoneauth1 import session @@ -198,7 +200,14 @@ class Adapter(object): def request(self, url, method, **kwargs): endpoint_filter = kwargs.setdefault('endpoint_filter', {}) self._set_endpoint_filter_kwargs(endpoint_filter) - + # NOTE(gmann): Convert r initlize the headers to + # CaseInsensitiveDict to make sure headers are + # case insensitive. + if kwargs.get('headers'): + kwargs['headers'] = requests.structures.CaseInsensitiveDict( + kwargs['headers']) + else: + kwargs['headers'] = requests.structures.CaseInsensitiveDict() if self.endpoint_override: kwargs.setdefault('endpoint_override', self.endpoint_override) diff --git a/keystoneauth1/session.py b/keystoneauth1/session.py index 935e022f..f4c400c0 100644 --- a/keystoneauth1/session.py +++ b/keystoneauth1/session.py @@ -753,6 +753,14 @@ class Session(object): else: split_loggers = None logger = logger or utils.get_logger(__name__) + # NOTE(gmann): Convert r initlize the headers to + # CaseInsensitiveDict to make sure headers are + # case insensitive. + if kwargs.get('headers'): + kwargs['headers'] = requests.structures.CaseInsensitiveDict( + kwargs['headers']) + else: + kwargs['headers'] = requests.structures.CaseInsensitiveDict() if connect_retries is None: connect_retries = self._connect_retries # HTTP 503 - Service Unavailable diff --git a/keystoneauth1/tests/unit/test_session.py b/keystoneauth1/tests/unit/test_session.py index 8817e4e7..3ded3f5c 100644 --- a/keystoneauth1/tests/unit/test_session.py +++ b/keystoneauth1/tests/unit/test_session.py @@ -1822,7 +1822,7 @@ class AdapterTest(utils.TestCase): with mock.patch.object(sess, 'request') as m: adapter.Adapter(sess, **adap_kwargs).get(url, **get_kwargs) m.assert_called_once_with(url, 'GET', endpoint_filter={}, - rate_semaphore=mock.ANY, + headers={}, rate_semaphore=mock.ANY, **exp_kwargs) # No default_microversion in Adapter, no microversion in get() @@ -1846,7 +1846,7 @@ class AdapterTest(utils.TestCase): with mock.patch.object(sess, 'request') as m: adapter.Adapter(sess, **adap_kwargs).get(url, **get_kwargs) m.assert_called_once_with(url, 'GET', endpoint_filter={}, - rate_semaphore=mock.ANY, + headers={}, rate_semaphore=mock.ANY, **exp_kwargs) # No raise_exc in Adapter or get()