Przeglądaj źródła

feat(api): officially support Retry-After: header for throttled requests

Peter Thomassen 4 lat temu
rodzic
commit
7519f13a62
2 zmienionych plików z 13 dodań i 9 usunięć
  1. 8 7
      api/desecapi/tests/test_throttling.py
  2. 5 2
      docs/rate-limits.rst

+ 8 - 7
api/desecapi/tests/test_throttling.py

@@ -39,7 +39,7 @@ class ThrottlingTestCase(TestCase):
         def do_test():
         def do_test():
             view = MockView.as_view()
             view = MockView.as_view()
             sum_delay = 0
             sum_delay = 0
-            for delay, count in counts:
+            for delay, count, max_wait in counts:
                 sum_delay += delay
                 sum_delay += delay
                 with mock.patch('desecapi.throttling.ScopedRatesThrottle.timer', return_value=time.time() + sum_delay):
                 with mock.patch('desecapi.throttling.ScopedRatesThrottle.timer', return_value=time.time() + sum_delay):
                     for _ in range(count):
                     for _ in range(count):
@@ -48,6 +48,7 @@ class ThrottlingTestCase(TestCase):
 
 
                     response = view(request)
                     response = view(request)
                     self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
                     self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
+                    self.assertTrue(max_wait - 1 <= float(response['Retry-After']) <= max_wait)
 
 
         cache.clear()
         cache.clear()
         request = self.factory.get('/')
         request = self.factory.get('/')
@@ -59,19 +60,19 @@ class ThrottlingTestCase(TestCase):
                     do_test()
                     do_test()
 
 
     def test_requests_are_throttled_4sec(self):
     def test_requests_are_throttled_4sec(self):
-        self._test_requests_are_throttled(['4/sec'], [(0, 4), (1, 4)])
+        self._test_requests_are_throttled(['4/sec'], [(0, 4, 1), (1, 4, 1)])
 
 
     def test_requests_are_throttled_4min(self):
     def test_requests_are_throttled_4min(self):
-        self._test_requests_are_throttled(['4/min'], [(0, 4)])
+        self._test_requests_are_throttled(['4/min'], [(0, 4, 60)])
 
 
     def test_requests_are_throttled_multiple(self):
     def test_requests_are_throttled_multiple(self):
-        self._test_requests_are_throttled(['5/s', '4/day'], [(0, 4)])
-        self._test_requests_are_throttled(['4/s', '5/day'], [(0, 4)])
+        self._test_requests_are_throttled(['5/s', '4/day'], [(0, 4, 86400)])
+        self._test_requests_are_throttled(['4/s', '5/day'], [(0, 4, 1)])
 
 
     def test_requests_are_throttled_multiple_cascade(self):
     def test_requests_are_throttled_multiple_cascade(self):
         # We test that we can do 4 requests in the first second and only 2 in the second second
         # We test that we can do 4 requests in the first second and only 2 in the second second
-        self._test_requests_are_throttled(['4/s', '6/day'], [(0, 4), (1, 2)])
+        self._test_requests_are_throttled(['4/s', '6/day'], [(0, 4, 1), (1, 2, 86400)])
 
 
     def test_requests_are_throttled_multiple_cascade_with_buckets(self):
     def test_requests_are_throttled_multiple_cascade_with_buckets(self):
         # We test that we can do 4 requests in the first second and only 2 in the second second
         # We test that we can do 4 requests in the first second and only 2 in the second second
-        self._test_requests_are_throttled(['4/s', '6/day'], [(0, 4), (1, 2)], buckets=['foo', 'bar'])
+        self._test_requests_are_throttled(['4/s', '6/day'], [(0, 4, 1), (1, 2, 86400)], buckets=['foo', 'bar'])

+ 5 - 2
docs/rate-limits.rst

@@ -8,8 +8,11 @@ ensure that the system load remains manageable, to avoid update rejections due
 to concurrent DNS updates on the same domain etc.
 to concurrent DNS updates on the same domain etc.
 
 
 Rate limits apply per account and are enforced in a sliding-window fashion.
 Rate limits apply per account and are enforced in a sliding-window fashion.
-For throttled requests, the server will respond with ``429 Too Many Requests``.
-The response body contains information on how long to wait.
+For throttled requests, the server will return status ``429 Too Many
+Requests`` and give a human-readable explanation in the response body,
+including how long to wait before making another request.  The number of
+seconds after which the next request will be allowed is also given by the
+``Retry-After`` header.
 
 
 **Example:** If the rate is 10/min and you make a request every second, the
 **Example:** If the rate is 10/min and you make a request every second, the
 11th request will be rejected.  You will then have to wait for 50 seconds,
 11th request will be rejected.  You will then have to wait for 50 seconds,