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
This commit is contained in:
Jens Harbott 2018-07-04 11:59:49 +00:00
parent e6c330892f
commit 3ffa54e446
4 changed files with 67 additions and 11 deletions

View File

@ -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 <https://bugs.launchpad.net/tempest/+bug/1616892>`_]
Tempest now allows tests to verify that an API responds with a
redirect.

View File

@ -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

View File

@ -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

View File

@ -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)