Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Provide retry-after guidance on throttled requests
Fixes bug 942874

Guide the caller's redrive strategy with a Retry-After hint in
the 413 response emitted when rate limits are exceeded.

The simplest format of Retry-After is used, i.e. number of seconds
as opposed to a HTTP time string.

Change-Id: I8b1a28f964a111008b1a88d9c0f96c5a0abd8314
  • Loading branch information
Eoghan Glynn committed Feb 29, 2012
1 parent 36100f6 commit 10c58c6
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 1 deletion.
12 changes: 11 additions & 1 deletion nova/api/openstack/wsgi.py
Expand Up @@ -18,6 +18,8 @@
import inspect
from xml.dom import minidom
from xml.parsers import expat
import math
import time

from lxml import etree
import webob
Expand Down Expand Up @@ -1075,7 +1077,8 @@ def __init__(self, message, details, retry_time):
"""
Initialize new `OverLimitFault` with relevant information.
"""
self.wrapped_exc = webob.exc.HTTPRequestEntityTooLarge()
hdrs = OverLimitFault._retry_after(retry_time)
self.wrapped_exc = webob.exc.HTTPRequestEntityTooLarge(headers=hdrs)
self.content = {
"overLimitFault": {
"code": self.wrapped_exc.status_int,
Expand All @@ -1084,6 +1087,13 @@ def __init__(self, message, details, retry_time):
},
}

@staticmethod
def _retry_after(retry_time):
delay = int(math.ceil(retry_time - time.time()))
retry_after = delay if delay > 0 else 0
headers = {'Retry-After': '%d' % retry_after}
return headers

@webob.dec.wsgify(RequestClass=Request)
def __call__(self, request):
"""
Expand Down
4 changes: 4 additions & 0 deletions nova/tests/api/openstack/compute/test_limits.py
Expand Up @@ -315,6 +315,10 @@ def test_limited_request_json(self):
response = request.get_response(self.app)
self.assertEqual(response.status_int, 413)

self.assertTrue('Retry-After' in response.headers)
retry_after = int(response.headers['Retry-After'])
self.assertAlmostEqual(retry_after, 60, 1)

body = json.loads(response.body)
expected = "Only 1 GET request(s) can be made to * every minute."
value = body["overLimitFault"]["details"].strip()
Expand Down

0 comments on commit 10c58c6

Please sign in to comment.