From d62d82b0fe4052983f9fa2408f08b9c5673e75bf Mon Sep 17 00:00:00 2001 From: Mykola Yakovliev Date: Thu, 31 May 2018 13:25:26 -0500 Subject: [PATCH] Generate correct url in api pagination Use protocol from X-Forwarded-Proto, and not from current endpoint. Change-Id: I3fafc4ef1cf56cb8f1cddc1a003a755e3f93c75c Closes-Bug: 1774460 --- neutron/api/api_common.py | 21 +++++++++++-- neutron/tests/unit/api/test_api_common.py | 38 +++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/neutron/api/api_common.py b/neutron/api/api_common.py index aac92bf368c..4296c0ba297 100644 --- a/neutron/api/api_common.py +++ b/neutron/api/api_common.py @@ -99,7 +99,8 @@ def get_previous_link(request, items, id_key): marker = items[0][id_key] params['marker'] = marker params['page_reverse'] = True - return "%s?%s" % (prepare_url(request.path_url), parse.urlencode(params)) + return "%s?%s" % (prepare_url(get_path_url(request)), + parse.urlencode(params)) def get_next_link(request, items, id_key): @@ -109,7 +110,8 @@ def get_next_link(request, items, id_key): marker = items[-1][id_key] params['marker'] = marker params.pop('page_reverse', None) - return "%s?%s" % (prepare_url(request.path_url), parse.urlencode(params)) + return "%s?%s" % (prepare_url(get_path_url(request)), + parse.urlencode(params)) def prepare_url(orig_url): @@ -125,6 +127,21 @@ def prepare_url(orig_url): return parse.urlunsplit(url_parts).rstrip('/') +def get_path_url(request): + """Return correct link if X-Forwarded-Proto exists in headers.""" + protocol = request.headers.get('X-Forwarded-Proto') + parsed = parse.urlparse(request.path_url) + + if protocol and parsed.scheme != protocol: + new_parsed = parse.ParseResult( + protocol, parsed.netloc, + parsed.path, parsed.params, + parsed.query, parsed.fragment) + return parse.urlunparse(new_parsed) + else: + return request.path_url + + def get_limit_and_marker(request): """Return marker, limit tuple from request. diff --git a/neutron/tests/unit/api/test_api_common.py b/neutron/tests/unit/api/test_api_common.py index 5e28a01352d..d7287deafe2 100644 --- a/neutron/tests/unit/api/test_api_common.py +++ b/neutron/tests/unit/api/test_api_common.py @@ -13,6 +13,7 @@ # under the License. from oslo_config import cfg +import webob from neutron.api import api_common from neutron.tests import base @@ -31,3 +32,40 @@ class PrepareUrlTestCase(base.BaseTestCase): requrl = 'http://neutron.example/sub/ports.json?test=1' expected = 'http://quantum.example/sub/ports.json?test=1' self.assertEqual(expected, api_common.prepare_url(requrl)) + + +class GetPathUrlTestCase(base.BaseTestCase): + + def test_no_headers(self): + base_http_url = 'http://neutron.example/sub/ports.json' + base_https_url = 'https://neutron.example/sub/ports.json' + path = '' + + http_req = webob.Request.blank(path, base_url=base_http_url) + https_req = webob.Request.blank(path, base_url=base_https_url) + + # should be unchanged + self.assertEqual(base_http_url, api_common.get_path_url(http_req)) + self.assertEqual(base_https_url, api_common.get_path_url(https_req)) + + def test_http_to_https(self): + base_url = 'http://neutron.example/sub/ports.json' + path = '' + + request = webob.Request.blank( + path, base_url=base_url, headers={'X-Forwarded-Proto': 'https'}) + + path_url = api_common.get_path_url(request) + # should replace http:// with https:// + self.assertTrue(path_url.startswith("https://")) + + def test_https_to_http(self): + base_url = 'https://neutron.example/sub/ports.json' + path = '' + + request = webob.Request.blank( + path, base_url=base_url, headers={'X-Forwarded-Proto': 'http'}) + + path_url = api_common.get_path_url(request) + # should replace https:// with http:// + self.assertTrue(path_url.startswith("http://"))