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)