diff --git a/heatclient/common/http.py b/heatclient/common/http.py index 978cf75d..3a268966 100644 --- a/heatclient/common/http.py +++ b/heatclient/common/http.py @@ -176,7 +176,7 @@ class HTTPClient(object): # Allow caller to specify not to follow redirects, in which case we # just return the redirect response. Useful for using stacks:lookup. - follow_redirects = kwargs.pop('follow_redirects', True) + redirect = kwargs.pop('redirect', True) # Since requests does not follow the RFC when doing redirection to sent # back the same method on a redirect we are simply bypassing it. For @@ -221,8 +221,8 @@ class HTTPClient(object): raise exc.from_response(resp) elif resp.status_code in (301, 302, 305): # Redirected. Reissue the request to the new location, - # unless caller specified follow_redirects=False - if follow_redirects: + # unless caller specified redirect=False + if redirect: location = resp.headers.get('location') path = self.strip_endpoint(location) resp = self._http_request(path, method, **kwargs) @@ -300,6 +300,7 @@ class SessionClient(adapter.LegacyJsonAdapter): """HTTP client based on Keystone client session.""" def request(self, url, method, **kwargs): + redirect = kwargs.get('redirect') kwargs.setdefault('user_agent', USER_AGENT) try: @@ -307,16 +308,18 @@ class SessionClient(adapter.LegacyJsonAdapter): except KeyError: pass - resp, body = super(SessionClient, self).request(url, method, - raise_exc=False, - **kwargs) + resp, body = super(SessionClient, self).request( + url, method, + raise_exc=False, + **kwargs) if 400 <= resp.status_code < 600: raise exc.from_response(resp) elif resp.status_code in (301, 302, 305): - location = resp.headers.get('location') - path = self.strip_endpoint(location) - resp = self.request(path, method, **kwargs) + if redirect: + location = resp.headers.get('location') + path = self.strip_endpoint(location) + resp = self.request(path, method, **kwargs) elif resp.status_code == 300: raise exc.from_response(resp) diff --git a/heatclient/tests/unit/test_common_http.py b/heatclient/tests/unit/test_common_http.py index 81b15999..bb2fa20b 100644 --- a/heatclient/tests/unit/test_common_http.py +++ b/heatclient/tests/unit/test_common_http.py @@ -1,4 +1,4 @@ -#-*- coding:utf-8 -*- +# -*- coding:utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -712,7 +712,7 @@ class SessionClientTest(testtools.TestCase): # Assert that the raised exception can be converted to string self.assertIsNotNone(six.text_type(e)) - def test_302_location(self): + def test_redirect_302_location(self): fake1 = fakes.FakeHTTPResponse( 302, 'OK', @@ -731,7 +731,7 @@ class SessionClientTest(testtools.TestCase): client = http.SessionClient(session=mock.ANY, auth=mock.ANY, endpoint_override='http://no.where/') - resp = client.request('', 'GET') + resp = client.request('', 'GET', redirect=True) self.assertEqual(200, resp.status_code) self.assertEqual({'Mount': 'Fuji'}, utils.get_response_body(resp)) @@ -740,7 +740,8 @@ class SessionClientTest(testtools.TestCase): self.assertEqual(('ishere', 'GET'), self.request.call_args_list[1][0]) for call in self.request.call_args_list: self.assertEqual({'user_agent': 'python-heatclient', - 'raise_exc': False}, call[1]) + 'raise_exc': False, + 'redirect': True}, call[1]) def test_302_location_no_endpoint(self): fake1 = fakes.FakeHTTPResponse( @@ -760,7 +761,7 @@ class SessionClientTest(testtools.TestCase): client = http.SessionClient(session=mock.ANY, auth=mock.ANY) - resp = client.request('', 'GET') + resp = client.request('', 'GET', redirect=True) self.assertEqual(200, resp.status_code) self.assertEqual({'Mount': 'Fuji'}, utils.get_response_body(resp)) @@ -770,9 +771,10 @@ class SessionClientTest(testtools.TestCase): 'GET'), self.request.call_args_list[1][0]) for call in self.request.call_args_list: self.assertEqual({'user_agent': 'python-heatclient', - 'raise_exc': False}, call[1]) + 'raise_exc': False, + 'redirect': True}, call[1]) - def test_302_no_location(self): + def test_redirect_302_no_location(self): fake = fakes.FakeHTTPResponse( 302, 'OK', @@ -784,9 +786,23 @@ class SessionClientTest(testtools.TestCase): client = http.SessionClient(session=mock.ANY, auth=mock.ANY) e = self.assertRaises(exc.InvalidEndpoint, - client.request, '', 'GET') + client.request, '', 'GET', redirect=True) self.assertEqual("Location not returned with 302", six.text_type(e)) + def test_no_redirect_302_no_location(self): + fake = fakes.FakeHTTPResponse( + 302, + 'OK', + {'location': 'http://no.where/ishere'}, + '' + ) + self.request.side_effect = [(fake, '')] + + client = http.SessionClient(session=mock.ANY, + auth=mock.ANY) + + self.assertEqual(fake, client.request('', 'GET')) + def test_300_error_response(self): fake = fakes.FakeHTTPResponse( 300, diff --git a/heatclient/v1/stacks.py b/heatclient/v1/stacks.py index bf7a7ba3..35984523 100644 --- a/heatclient/v1/stacks.py +++ b/heatclient/v1/stacks.py @@ -235,9 +235,8 @@ class StackChildManager(base.BaseManager): # We want to capture the redirect, not actually get the stack, # since all we want is the stacks:lookup response to get the # fully qualified ID, and not all users are allowed to do the - # redirected stacks:show, so pass follow_redirects=False - resp = self.client.get('/stacks/%s' % stack_id, - follow_redirects=False) + # redirected stacks:show, so pass redirect=False + resp = self.client.get('/stacks/%s' % stack_id, redirect=False) location = resp.headers.get('location') path = self.client.strip_endpoint(location) return path[len('/stacks/'):]