Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
django-rest-framework
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
django-rest-framework
Commits
323d52e7
Commit
323d52e7
authored
Jun 14, 2011
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Bits of cleaning up for the throttling
parent
fb26b11a
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
77 additions
and
43 deletions
+77
-43
djangorestframework/permissions.py
+63
-31
djangorestframework/tests/throttling.py
+14
-12
No files found.
djangorestframework/permissions.py
View file @
323d52e7
"""
The :mod:`permissions` module bundles a set of permission classes that are used
for checking if a request passes a certain set of constraints. You can assign a permision
for checking if a request passes a certain set of constraints. You can assign a permis
s
ion
class to your view by setting your View's :attr:`permissions` class attribute.
"""
...
...
@@ -26,14 +26,14 @@ _403_FORBIDDEN_RESPONSE = ErrorResponse(
{
'detail'
:
'You do not have permission to access this resource. '
+
'You may need to login or otherwise authenticate the request.'
})
_503_
THROTTLED_RESPONS
E
=
ErrorResponse
(
_503_
SERVICE_UNAVAILABL
E
=
ErrorResponse
(
status
.
HTTP_503_SERVICE_UNAVAILABLE
,
{
'detail'
:
'request was throttled'
})
class
ConfigurationException
(
BaseException
):
"""To alert for bad configuration desic
ions as a convenience."""
pass
"""To alert for bad configuration decis
ions as a convenience."""
pass
class
BasePermission
(
object
):
...
...
@@ -93,38 +93,56 @@ class IsUserOrIsAnonReadOnly(BasePermission):
self
.
view
.
method
!=
'HEAD'
):
raise
_403_FORBIDDEN_RESPONSE
class
BaseThrottle
(
BasePermission
):
"""
Rate throttling of requests.
The rate (requests / seconds) is set by a :attr:`throttle` attribute on the ``View`` class.
The attribute is a string of the form 'number of requests/period'. Period must be an element
of (sec, min, hour, day)
The rate (requests / seconds) is set by a :attr:`throttle` attribute
on the :class:`.View` class. The attribute is a string of the form 'number of
requests/period'.
Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')
Previous request information used for throttling is stored in the cache.
"""
attr_name
=
'throttle'
default
=
'0/sec'
timer
=
time
.
time
def
get_cache_key
(
self
):
"""Should return the cache-key corresponding to the semantics of the class that implements
the throttling behaviour.
"""
Should return a unique cache-key which can be used for throttling.
Muse be overridden.
"""
pass
def
check_permission
(
self
,
auth
):
num
,
period
=
getattr
(
self
.
view
,
'throttle'
,
'0/sec'
)
.
split
(
'/'
)
"""
Check the throttling.
Return `None` or raise an :exc:`.ErrorResponse`.
"""
num
,
period
=
getattr
(
self
.
view
,
self
.
attr_name
,
self
.
default
)
.
split
(
'/'
)
self
.
num_requests
=
int
(
num
)
self
.
duration
=
{
's'
:
1
,
'm'
:
60
,
'h'
:
3600
,
'd'
:
86400
}[
period
[
0
]]
self
.
auth
=
auth
self
.
check_throttle
()
def
check_throttle
(
self
):
"""On success calls `throttle_success`. On failure calls `throttle_failure`. """
"""
Implement the check to see if the request should be throttled.
On success calls :meth:`throttle_success`.
On failure calls :meth:`throttle_failure`.
"""
self
.
key
=
self
.
get_cache_key
()
self
.
history
=
cache
.
get
(
self
.
key
,
[])
self
.
now
=
time
.
time
()
self
.
now
=
self
.
timer
()
# Drop any requests from the history which have now passed the throttle duration
while
self
.
history
and
self
.
history
[
0
]
<
self
.
now
-
self
.
duration
:
# Drop any requests from the history which have now passed the
# throttle duration
while
self
.
history
and
self
.
history
[
0
]
<=
self
.
now
-
self
.
duration
:
self
.
history
.
pop
()
if
len
(
self
.
history
)
>=
self
.
num_requests
:
...
...
@@ -133,18 +151,28 @@ class BaseThrottle(BasePermission):
self
.
throttle_success
()
def
throttle_success
(
self
):
"""Inserts the current request's timesatmp along with the key into the cache."""
"""
Inserts the current request's timestamp along with the key
into the cache.
"""
self
.
history
.
insert
(
0
,
self
.
now
)
cache
.
set
(
self
.
key
,
self
.
history
,
self
.
duration
)
def
throttle_failure
(
self
):
"""Raises a 503 """
raise
_503_THROTTLED_RESPONSE
"""
Called when a request to the API has failed due to throttling.
Raises a '503 service unavailable' response.
"""
raise
_503_SERVICE_UNAVAILABLE
class
PerUserThrottling
(
BaseThrottle
):
"""
The user id will be used as a unique identifier if the user is authenticated.
For anonymous requests, the IP address of the client will be used.
Limits the rate of API calls that may be made by a given user.
The user id will be used as a unique identifier if the user is
authenticated. For anonymous requests, the IP address of the client will
be used.
"""
def
get_cache_key
(
self
):
...
...
@@ -152,24 +180,29 @@ class PerUserThrottling(BaseThrottle):
ident
=
str
(
self
.
auth
)
else
:
ident
=
self
.
view
.
request
.
META
.
get
(
'REMOTE_ADDR'
,
None
)
return
'throttle_
%
s'
%
ident
return
'throttle_user_
%
s'
%
ident
class
PerViewThrottling
(
BaseThrottle
):
"""
The class name of the cuurent view will be used as a unique identifier.
Limits the rate of API calls that may be used on a given view.
The class name of the view is used as a unique identifier to
throttle against.
"""
def
get_cache_key
(
self
):
return
'throttle_
%
s'
%
self
.
view
.
__class__
.
__name__
return
'throttle_view_
%
s'
%
self
.
view
.
__class__
.
__name__
class
PerResourceThrottling
(
BaseThrottle
):
"""
The class name of the cuurent resource will be used as a unique identifier.
Raises :exc:`ConfigurationException` if no resource attribute is set on the view class.
Limits the rate of API calls that may be used against all views on
a given resource.
The class name of the resource is used as a unique identifier to
throttle against.
"""
def
get_cache_key
(
self
):
if
self
.
view
.
resource
!=
None
:
return
'throttle_
%
s'
%
self
.
view
.
resource
.
__class__
.
__name__
raise
ConfigurationException
(
"A per-resource throttle was set to a view that does not have a resource."
)
\ No newline at end of file
return
'throttle_resource_
%
s'
%
self
.
view
.
resource
.
__class__
.
__name__
djangorestframework/tests/throttling.py
View file @
323d52e7
"""
Tests for the throttling implementations in the permissions module.
"""
import
time
from
django.conf.urls.defaults
import
patterns
...
...
@@ -44,12 +47,20 @@ class ThrottlingTests(TestCase):
self
.
assertEqual
(
503
,
response
.
status_code
)
def
test_request_throttling_expires
(
self
):
"""Ensure request rate is limited for a limited duration only"""
"""
Ensure request rate is limited for a limited duration only
"""
# Explicitly set the timer, overridding time.time()
MockView
.
permissions
[
0
]
.
timer
=
lambda
self
:
0
request
=
self
.
factory
.
get
(
'/'
)
for
dummy
in
range
(
4
):
response
=
MockView
.
as_view
()(
request
)
self
.
assertEqual
(
503
,
response
.
status_code
)
time
.
sleep
(
1
)
# Advance the timer by one second
MockView
.
permissions
[
0
]
.
timer
=
lambda
self
:
1
response
=
MockView
.
as_view
()(
request
)
self
.
assertEqual
(
200
,
response
.
status_code
)
...
...
@@ -63,7 +74,7 @@ class ThrottlingTests(TestCase):
self
.
assertEqual
(
expect
,
response
.
status_code
)
def
test_request_throttling_is_per_user
(
self
):
"""Ensure request rate is only limited per user, not globally for PerUserTrottles"""
"""Ensure request rate is only limited per user, not globally for PerUserT
h
rottles"""
self
.
ensure_is_throttled
(
MockView
,
200
)
def
test_request_throttling_is_per_view
(
self
):
...
...
@@ -73,12 +84,4 @@ class ThrottlingTests(TestCase):
def
test_request_throttling_is_per_resource
(
self
):
"""Ensure request rate is limited globally per Resource for PerResourceThrottles"""
self
.
ensure_is_throttled
(
MockView3
,
503
)
def
test_raises_no_resource_found
(
self
):
"""Ensure an Exception is raised when someone sets at per-resource throttle
on a view with no resource set."""
request
=
self
.
factory
.
get
(
'/'
)
view
=
MockView2
.
as_view
()
self
.
assertRaises
(
ConfigurationException
,
view
,
request
)
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment