Add http_timeout config option

Otherwise, requests may wait forever for a response.

Now, we will wait at most 10 seconds by default, and allow operators to
adjust that to between 0 and 60 seconds.

This option closely mirrors the http_connect_timeout option in
Keystone's authtoken middleware.

Change-Id: I43fe784551abe6de790c781d0addfa25519a1f55
This commit is contained in:
Tim Burke 2016-06-08 12:31:28 -07:00
parent 8878257088
commit e7a2b88191
3 changed files with 55 additions and 1 deletions

View File

@ -149,6 +149,9 @@ reseller_prefix = AUTH_
# Keystone server details
auth_uri = http://keystonehost:35357/
# Connect/read timeout to use when communicating with Keystone
http_timeout = 10.0
# SSL-related options
#insecure = False
#certfile =

View File

@ -59,6 +59,9 @@ class S3Token(object):
self._app = app
self._logger = logging.getLogger(conf.get('log_name', __name__))
self._logger.debug('Starting the %s component', PROTOCOL_NAME)
self._timeout = float(conf.get('http_timeout', '10.0'))
if not (0 < self._timeout <= 60):
raise ValueError('http_timeout must be between 0 and 60 seconds')
self._reseller_prefix = conf.get('reseller_prefix', 'AUTH_')
# where to find the auth service (we use this to validate tokens)
@ -119,7 +122,8 @@ class S3Token(object):
try:
response = requests.post('%s/v2.0/s3tokens' % self._request_uri,
headers=headers, data=creds_json,
verify=self._verify)
verify=self._verify,
timeout=self._timeout)
except requests.exceptions.RequestException as e:
self._logger.info('HTTP connection exception: %s', e)
resp = self._deny_request('InvalidURI')

View File

@ -255,6 +255,53 @@ class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase):
self.assertEqual('Either auth_uri or auth_host required',
cm.exception.message)
@mock.patch.object(requests, 'post')
def test_http_timeout(self, MOCK_REQUEST):
self.middleware = s3_token.filter_factory({
'http_timeout': '2',
'auth_uri': 'http://example.com',
})(FakeApp())
MOCK_REQUEST.return_value = TestResponse({
'status_code': 201,
'text': json.dumps(GOOD_RESPONSE)})
req = Request.blank('/v1/AUTH_cfa/c/o')
req.headers['Authorization'] = 'AWS access:signature'
req.headers['X-Storage-Token'] = 'token'
req.get_response(self.middleware)
self.assertTrue(MOCK_REQUEST.called)
mock_args, mock_kwargs = MOCK_REQUEST.call_args
self.assertEqual(mock_kwargs['timeout'], 2)
def test_http_timeout_option(self):
good_values = ['1', '5.3', '10', '.001']
for val in good_values:
middleware = s3_token.filter_factory({
'http_timeout': val,
'auth_uri': 'http://example.com',
})(FakeApp())
self.assertEqual(float(val), middleware._timeout)
bad_values = ['1, 4', '-3', '100', 'foo', '0']
for val in bad_values:
with self.assertRaises(ValueError) as ctx:
s3_token.filter_factory({
'http_timeout': val,
'auth_uri': 'http://example.com',
})(FakeApp())
self.assertTrue(ctx.exception.args[0].startswith((
'invalid literal for float():',
'could not convert string to float:',
'http_timeout must be between 0 and 60 seconds',
)), 'Unexpected error message: %s' % ctx.exception)
# default is 10 seconds
middleware = s3_token.filter_factory({
'auth_uri': 'http://example.com'})(FakeApp())
self.assertEqual(10, middleware._timeout)
def test_unicode_path(self):
url = u'/v1/AUTH_cfa/c/euro\u20ac'.encode('utf8')
req = Request.blank(urllib.parse.quote(url))