From 3ffa54e446ed2a8bf6758343a3130ae424d3ddc4 Mon Sep 17 00:00:00 2001 From: Jens Harbott Date: Wed, 4 Jul 2018 11:59:49 +0000 Subject: [PATCH] Allow to create a rest_client not following redirects Some tests need to verify that the first response from an API is indeed a redirect, this can not be done if the client automatically follows redirects. Introduce a parameter that allows consumers to disable the default behaviour, so that they can see the 301 response instead. Change-Id: I366fa8d1971cd7502a1cd985f5ee6ad5e1ecb216 Closes-Bug: 1616892 --- .../add-redirect-param-bea1f6fbce629c70.yaml | 16 +++++++++++ tempest/lib/common/http.py | 28 +++++++++++++------ tempest/lib/common/rest_client.py | 7 +++-- tempest/tests/lib/common/test_http.py | 27 ++++++++++++++++++ 4 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 releasenotes/notes/add-redirect-param-bea1f6fbce629c70.yaml diff --git a/releasenotes/notes/add-redirect-param-bea1f6fbce629c70.yaml b/releasenotes/notes/add-redirect-param-bea1f6fbce629c70.yaml new file mode 100644 index 0000000000..f245dcb495 --- /dev/null +++ b/releasenotes/notes/add-redirect-param-bea1f6fbce629c70.yaml @@ -0,0 +1,16 @@ +--- +features: + - | + A new parameter ``follow_redirects`` has been added to the class + ``RestClient``, which is passed through to ``ClosingHttp`` or + ``ClosingProxyHttp`` respectively. The default value is ``True`` + which corresponds to the previous behaviour of following up to five + redirections before returning a response. Setting + ``follow_redirects = False`` allows to disable this behaviour, so + that any redirect that is received is directly returned to the caller. + This allows tests to verify that an API is responding with a redirect. +fixes: + - | + [`bug 1616892 `_] + Tempest now allows tests to verify that an API responds with a + redirect. diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py index 738c37f7eb..8c1a80222d 100644 --- a/tempest/lib/common/http.py +++ b/tempest/lib/common/http.py @@ -19,7 +19,8 @@ import urllib3 class ClosingProxyHttp(urllib3.ProxyManager): def __init__(self, proxy_url, disable_ssl_certificate_validation=False, - ca_certs=None, timeout=None): + ca_certs=None, timeout=None, follow_redirects=True): + self.follow_redirects = follow_redirects kwargs = {} if disable_ssl_certificate_validation: @@ -50,9 +51,14 @@ class ClosingProxyHttp(urllib3.ProxyManager): new_headers = dict(original_headers, connection='close') new_kwargs = dict(kwargs, headers=new_headers) - # Follow up to 5 redirections. Don't raise an exception if - # it's exceeded but return the HTTP 3XX response instead. - retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5) + if self.follow_redirects: + # Follow up to 5 redirections. Don't raise an exception if + # it's exceeded but return the HTTP 3XX response instead. + retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5) + else: + # Do not follow redirections. Don't raise an exception if + # a redirect is found, but return the HTTP 3XX response instead. + retry = urllib3.util.Retry(redirect=False) r = super(ClosingProxyHttp, self).request(method, url, retries=retry, *args, **new_kwargs) return Response(r), r.data @@ -60,7 +66,8 @@ class ClosingProxyHttp(urllib3.ProxyManager): class ClosingHttp(urllib3.poolmanager.PoolManager): def __init__(self, disable_ssl_certificate_validation=False, - ca_certs=None, timeout=None): + ca_certs=None, timeout=None, follow_redirects=True): + self.follow_redirects = follow_redirects kwargs = {} if disable_ssl_certificate_validation: @@ -93,9 +100,14 @@ class ClosingHttp(urllib3.poolmanager.PoolManager): new_headers = dict(original_headers, connection='close') new_kwargs = dict(kwargs, headers=new_headers) - # Follow up to 5 redirections. Don't raise an exception if - # it's exceeded but return the HTTP 3XX response instead. - retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5) + if self.follow_redirects: + # Follow up to 5 redirections. Don't raise an exception if + # it's exceeded but return the HTTP 3XX response instead. + retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5) + else: + # Do not follow redirections. Don't raise an exception if + # a redirect is found, but return the HTTP 3XX response instead. + retry = urllib3.util.Retry(redirect=False) r = super(ClosingHttp, self).request(method, url, retries=retry, *args, **new_kwargs) return Response(r), r.data diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py index 22276d4d4f..fd1c91ede4 100644 --- a/tempest/lib/common/rest_client.py +++ b/tempest/lib/common/rest_client.py @@ -70,6 +70,7 @@ class RestClient(object): :param str http_timeout: Timeout in seconds to wait for the http request to return :param str proxy_url: http proxy url to use. + :param bool follow_redirects: Set to false to stop following redirects. """ # The version of the API this client implements @@ -82,7 +83,7 @@ class RestClient(object): build_interval=1, build_timeout=60, disable_ssl_certificate_validation=False, ca_certs=None, trace_requests='', name=None, http_timeout=None, - proxy_url=None): + proxy_url=None, follow_redirects=True): self.auth_provider = auth_provider self.service = service self.region = region @@ -107,11 +108,11 @@ class RestClient(object): self.http_obj = http.ClosingProxyHttp( proxy_url, disable_ssl_certificate_validation=dscv, ca_certs=ca_certs, - timeout=http_timeout) + timeout=http_timeout, follow_redirects=follow_redirects) else: self.http_obj = http.ClosingHttp( disable_ssl_certificate_validation=dscv, ca_certs=ca_certs, - timeout=http_timeout) + timeout=http_timeout, follow_redirects=follow_redirects) def get_headers(self, accept_type=None, send_type=None): """Return the default headers which will be used with outgoing requests diff --git a/tempest/tests/lib/common/test_http.py b/tempest/tests/lib/common/test_http.py index 02436e084d..336ef4a27d 100644 --- a/tempest/tests/lib/common/test_http.py +++ b/tempest/tests/lib/common/test_http.py @@ -167,3 +167,30 @@ class TestClosingProxyHttp(TestClosingHttp): '%s://%s:%i' % (proxy.scheme, proxy.host, proxy.port)) + + +class TestClosingHttpRedirects(base.TestCase): + def setUp(self): + super(TestClosingHttpRedirects, self).setUp() + + def test_redirect_default(self): + connection = http.ClosingHttp() + self.assertTrue(connection.follow_redirects) + + def test_redirect_off(self): + connection = http.ClosingHttp(follow_redirects=False) + self.assertFalse(connection.follow_redirects) + + +class TestClosingProxyHttpRedirects(base.TestCase): + def setUp(self): + super(TestClosingProxyHttpRedirects, self).setUp() + + def test_redirect_default(self): + connection = http.ClosingProxyHttp(proxy_url=PROXY_URL) + self.assertTrue(connection.follow_redirects) + + def test_redirect_off(self): + connection = http.ClosingProxyHttp(follow_redirects=False, + proxy_url=PROXY_URL) + self.assertFalse(connection.follow_redirects)