diff --git a/mixmatch/proxy.py b/mixmatch/proxy.py index 0cec502..73cee77 100644 --- a/mixmatch/proxy.py +++ b/mixmatch/proxy.py @@ -57,7 +57,7 @@ def is_json_response(response): def is_token_header_key(string): - return string.lower() in ['x-auth-token', 'x-service-token'] + return string in ['X-AUTH-TOKEN', 'X-SERVICE-TOKEN'] def strip_tokens_from_headers(headers): @@ -92,7 +92,7 @@ class RequestDetails(object): self.resource_type = utils.safe_pop(local_path) # this self.resource_id = utils.pop_if_uuid(local_path) # and this self.token = headers.get('X-AUTH-TOKEN', None) - self.headers = dict(headers) + self.headers = {k.upper(): v for k, v in dict(headers).items()} self.path = orig_path self.args = dict(request.args) # NOTE(jfreud): if chunked transfer, body must be accessed through @@ -219,7 +219,7 @@ class RequestHandler(object): final_response = flask.Response( text, response.status_code, - headers=self._prepare_headers(response.headers) + headers=self._prepare_headers(response.headers, fix_case=True) ) LOG.info(format_for_log(title='Response from proxy', status_code=final_response.status_code, @@ -295,15 +295,20 @@ class RequestHandler(object): return self._forward() @staticmethod - def _prepare_headers(user_headers): + def _prepare_headers(user_headers, fix_case=False): + # NOTE(jfreud): because this function may be called with either request + # headers or response headers, sometimes the header keys may not be + # already capitalized + if fix_case: + user_headers = {k.upper(): v for k, v in + dict(user_headers).items()} headers = dict() - headers['Accept'] = user_headers.get('Accept', '') - headers['Content-Type'] = user_headers.get('Content-Type', '') - accepted_headers = ['openstack-api-version'] + headers['ACCEPT'] = user_headers.get('ACCEPT', '') + headers['CONTENT-TYPE'] = user_headers.get('CONTENT-TYPE', '') + accepted_headers = ['OPENSTACK-API-VERSION'] for key, value in user_headers.items(): - k = key.lower() - if ((k.startswith('x-') and not is_token_header_key(key)) or - k in accepted_headers): + if ((key.startswith('X-') and not is_token_header_key(key)) or + key in accepted_headers): headers[key] = value return headers @@ -321,7 +326,7 @@ class RequestHandler(object): @utils.CachedProperty def chunked(self): - encoding = self.details.headers.get('Transfer-Encoding', '') + encoding = self.details.headers.get('TRANSFER-ENCODING', '') return encoding.lower() == 'chunked' @utils.CachedProperty diff --git a/mixmatch/tests/unit/test_request_details.py b/mixmatch/tests/unit/test_request_details.py new file mode 100644 index 0000000..30c1341 --- /dev/null +++ b/mixmatch/tests/unit/test_request_details.py @@ -0,0 +1,31 @@ +# Copyright 2017 Massachusetts Open Cloud +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from testtools import testcase + +from mixmatch import proxy + + +class TestRequestDetails(testcase.TestCase): + + def test_capitalized_headers(self): + normal_headers = {"Mm-Service-Provider": "default", + "X-Auth-Token": "tok", + "Transfer-Encoding": "chunked"} + with proxy.app.test_request_context(): + rd = proxy.RequestDetails("GET", "image/v2/images", normal_headers) + expected = {"MM-SERVICE-PROVIDER": "default", + "X-AUTH-TOKEN": "tok", + "TRANSFER-ENCODING": "chunked"} + self.assertEqual(expected, rd.headers) diff --git a/mixmatch/tests/unit/test_request_handler.py b/mixmatch/tests/unit/test_request_handler.py index ecbb491..48ce23d 100644 --- a/mixmatch/tests/unit/test_request_handler.py +++ b/mixmatch/tests/unit/test_request_handler.py @@ -31,42 +31,51 @@ class TestRequestHandler(BaseTest): def test_prepare_headers(self): user_headers = { - 'x-auth-token': 'auth token', - 'x-service-token': 'service token', 'X-AUTH-TOKEN': 'AUTH TOKEN', 'X-SERVICE-TOKEN': 'SERVICE TOKEN', - 'x-tra cheese': 'extra cheese', - 'x-goth-token': 'x-auth-token', + 'X-TRA CHEESE': 'extra cheese', + 'X-GOTH-TOKEN': 'x-auth-token', 'X-MEN': 'X MEN', - 'y-men': 'y men', - 'extra cheese': 'x-tra cheese', - 'y-auth-token': 'x-auth-token', - 'xauth-token': 'x-auth-token', - 'start-x': 'startx', + 'Y-MEN': 'y men', + 'EXTRA CHEESE': 'x-tra cheese', + 'Y-AUTH-TOKEN': 'x-auth-token', + 'XAUTH-TOKEN': 'x-auth-token', + 'START-X': 'startx', - 'OpenStack-API-Version': 'volume 3.0' + 'OPENSTACK-API-VERSION': 'volume 3.0' } expected_headers = { - 'x-tra cheese': 'extra cheese', - 'x-goth-token': 'x-auth-token', + 'X-TRA CHEESE': 'extra cheese', + 'X-GOTH-TOKEN': 'x-auth-token', 'X-MEN': 'X MEN', - 'Accept': '', - 'Content-Type': '', - 'OpenStack-API-Version': 'volume 3.0' + 'ACCEPT': '', + 'CONTENT-TYPE': '', + 'OPENSTACK-API-VERSION': 'volume 3.0' } headers = proxy.RequestHandler._prepare_headers(user_headers) self.assertEqual(expected_headers, headers) + def test_prepare_headers_fix_case(self): + user_headers = { + 'X-Auth-Token': 'AUTH TOKEN', + 'X-Service-Token': 'SERVICE TOKEN', + 'Openstack-Api-Version': 'volume 3.0' + } + headers = proxy.RequestHandler._prepare_headers(user_headers) + self.assertTrue('OPENSTACK-API-VERSION' not in headers.keys() and + 'Openstack-Api-Version' not in headers.keys()) + headers = proxy.RequestHandler._prepare_headers(user_headers, True) + self.assertTrue('OPENSTACK-API-VERSION' in headers.keys() and + 'Openstack-Api-Version' not in headers.keys()) + def test_strip_tokens_from_logs(self): token = uuid.uuid4() headers = { - 'x-auth-token': token, 'X-AUTH-TOKEN': token, - 'not a token': 'not a token', - 'X-Service-Token': token, - 'x-service-token': token + 'NOT A TOKEN': 'not a token', + 'X-SERVICE-TOKEN': token, } stripped_headers = proxy.strip_tokens_from_headers(headers) self.assertFalse(token in stripped_headers.values())