From 8bc87c62a118c75a6cbf69c0edfa3ba5acbf3950 Mon Sep 17 00:00:00 2001 From: Eoghan Glynn Date: Tue, 28 Feb 2012 20:22:19 +0000 Subject: 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 --- nova/api/openstack/wsgi.py | 12 +++++++++++- nova/tests/api/openstack/compute/test_limits.py | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index 8f0259024..047b68956 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -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 @@ -1075,7 +1077,8 @@ class OverLimitFault(webob.exc.HTTPException): """ 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, @@ -1084,6 +1087,13 @@ class OverLimitFault(webob.exc.HTTPException): }, } + @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): """ diff --git a/nova/tests/api/openstack/compute/test_limits.py b/nova/tests/api/openstack/compute/test_limits.py index 26e0094c0..eb4ab00c4 100644 --- a/nova/tests/api/openstack/compute/test_limits.py +++ b/nova/tests/api/openstack/compute/test_limits.py @@ -315,6 +315,10 @@ class LimitMiddlewareTest(BaseLimitTestSuite): 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() -- cgit