From d6fcf7459435077b525123e8b78e553070d5c070 Mon Sep 17 00:00:00 2001 From: Kota Tsuyuzaki Date: Sat, 16 Sep 2017 04:58:31 +0900 Subject: [PATCH] Make gate keeper to save relative location header path Why we need this: Some middlewares want to keep HTTP Location header as relative path (e.g. using Load balancer in front of proxy). What is the problem in current Swift: Current Swift already has the flag to keep it as relative when returning the reponse using swift.common.swob.Response. However, auth_token middleware, that is from keystonemiddleware, unfortunately can change the relative path to absolute because of using webob instead of swob. What this patch is doing: Make gate_keeper able to re-transform the location header from absolute path to relative path if 'swift.leave_relative_location' is explicitely set because gate_keeper should be the most left side middleware except catch_errors middleware in the pipeline. Change-Id: Ic634c3f1b1e26635206d5a54df8b15354e8df163 --- swift/common/middleware/gatekeeper.py | 21 +++++++++++++ .../unit/common/middleware/test_gatekeeper.py | 31 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/swift/common/middleware/gatekeeper.py b/swift/common/middleware/gatekeeper.py index e5df5bf44c..991ec86cd9 100644 --- a/swift/common/middleware/gatekeeper.py +++ b/swift/common/middleware/gatekeeper.py @@ -36,6 +36,7 @@ from swift.common.utils import get_logger, config_true_value from swift.common.request_helpers import ( remove_items, get_sys_meta_prefix, OBJECT_TRANSIENT_SYSMETA_PREFIX ) +from six.moves.urllib.parse import urlsplit import re #: A list of python regular expressions that will be used to @@ -89,9 +90,29 @@ class GatekeeperMiddleware(object): [('X-Timestamp', ts)]) def gatekeeper_response(status, response_headers, exc_info=None): + def fixed_response_headers(): + def relative_path(value): + parsed = urlsplit(v) + new_path = parsed.path + if parsed.query: + new_path += ('?%s' % parsed.query) + if parsed.fragment: + new_path += ('#%s' % parsed.fragment) + return new_path + + if not env.get('swift.leave_relative_location'): + return response_headers + else: + return [ + (k, v) if k.lower() != 'location' else + (k, relative_path(v)) for (k, v) in response_headers + ] + + response_headers = fixed_response_headers() removed = filter( lambda h: self.outbound_condition(h[0]), response_headers) + if removed: self.logger.debug('removed response headers: %s' % removed) new_headers = filter( diff --git a/test/unit/common/middleware/test_gatekeeper.py b/test/unit/common/middleware/test_gatekeeper.py index 888aeb6204..d8c09368c3 100644 --- a/test/unit/common/middleware/test_gatekeeper.py +++ b/test/unit/common/middleware/test_gatekeeper.py @@ -215,5 +215,36 @@ class TestGatekeeper(unittest.TestCase): for app_hdrs in ({}, self.forbidden_headers_out): self._test_duplicate_headers_not_removed(method, app_hdrs) + def _test_location_header(self, location_path): + headers = {'Location': location_path} + req = Request.blank( + '/v/a/c', environ={'REQUEST_METHOD': 'GET', + 'swift.leave_relative_location': True}) + + class SelfishApp(FakeApp): + def __call__(self, env, start_response): + self.req = Request(env) + resp = Response(request=self.req, body='FAKE APP', + headers=self.headers) + # like webob, middlewares in the pipeline may rewrite + # location header from relative to absolute + resp.location = resp.absolute_location() + return resp(env, start_response) + + selfish_app = SelfishApp(headers=headers) + + app = self.get_app(selfish_app, {}) + resp = req.get_response(app) + self.assertEqual('200 OK', resp.status) + self.assertIn('Location', resp.headers) + self.assertEqual(resp.headers['Location'], location_path) + + def test_location_header_fixed(self): + self._test_location_header('/v/a/c/o2') + self._test_location_header('/v/a/c/o2?query=path&query2=doit') + self._test_location_header('/v/a/c/o2?query=path#test') + self._test_location_header('/v/a/c/o2;whatisparam?query=path#test') + + if __name__ == '__main__': unittest.main()