From c50847cabfd6c8506ec87649e13202af5d6af3e2 Mon Sep 17 00:00:00 2001 From: Christopher Grebs Date: Fri, 26 Jul 2019 16:28:44 +0200 Subject: [PATCH] Handle 'None' return value of wait() properly during throttling. Fixes #6836 --- rest_framework/views.py | 12 +++++++++++- tests/test_throttling.py | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/rest_framework/views.py b/rest_framework/views.py index 832f17233..5f52adaa5 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -356,7 +356,17 @@ class APIView(View): throttle_durations.append(throttle.wait()) if throttle_durations: - self.throttled(request, max(throttle_durations)) + # Filter out `None` values which may happen in case of config / rate + # changes, see #1438 + durations = [ + duration for duration in throttle_durations + if duration is not None + ] + + if durations: + self.throttled(request, max(durations)) + else: + self.throttled(request, None) def determine_version(self, request, *args, **kwargs): """ diff --git a/tests/test_throttling.py b/tests/test_throttling.py index 3c172e263..2cc60a1a7 100644 --- a/tests/test_throttling.py +++ b/tests/test_throttling.py @@ -159,6 +159,26 @@ class ThrottlingTests(TestCase): assert response.status_code == 429 assert int(response['retry-after']) == 58 + def test_handle_negative_throttle_value(self): + self.set_throttle_timer(MockView_DoubleThrottling, 0) + request = self.factory.get('/') + for dummy in range(24): + response = MockView_DoubleThrottling.as_view()(request) + assert response.status_code == 429 + assert int(response['retry-after']) == 60 + + previous_rate = User3SecRateThrottle.rate + User3SecRateThrottle.rate = '1/sec' + + for dummy in range(24): + response = MockView_DoubleThrottling.as_view()(request) + + assert response.status_code == 429 + assert int(response['retry-after']) == 60 + + # reset + User3SecRateThrottle.rate = previous_rate + def ensure_response_header_contains_proper_throttle_field(self, view, expected_headers): """ Ensure the response returns an Retry-After field with status and next attributes