Как-то раз пришлось использовать чужое django-app, в котором было много форм, методов и всего остального. И, что само собой разумеется, автор решил не заморачиваться и не делать никакой защиты от спамеров, или от любителей понажимать F5 на тяжелой форме. Переписывать и форкать у меня желания не было, поэтому решил написать middleware, режущий кислород нехорошим людям.
Проще показать пример конфига:
Остальные примеры с описанием под катом.
Конфиг состоит из секций, самая верхняя — fallback, применяется для всего сайта в целом, в случае, если не нашлось более детализированного правила.
В этом примере для запросов всех типов установлено ограничение в 1 запрос в секунду, post — запрос раз в 10 секунд.
congestion может быть uri, или вьюхой. В данном случае будет вызвана вьюха, живущая по соседству:
Это позволит сказать юзеру, что он шалит и, к примеру, должен подождать N секунд. Вьюху можно снабдить скриптом с прогрессбаром, который автоматически зарелоадит форму. В congestion_bundle передается оригинальная вьюха и весь набор ее аргументов, дабы можно было сказать юзеру что-то более детальное.
Можно отключить POST на всем сайте. Тогда юзер увидит HttpResponseBadRequest:
А можно отключить POST вот так, тогда юзер будет отправляться в корень:
Если простого редиректа недостаточно, то можно сделать свой обработчик для maintenance-mode:
Может быть так, что нужно блокировать вьюху только в определенных случаях, например, для блокирования одного топика в форуме, или для еще какого-то частного случая. Тогда можно написать кастомный обработчик для правила:
Он должен возвращать кортеж из названия ключа и таймаута. Таймаутом может снова быть int(), False, view, или uri.
В основном имеет аналогичный синтаксис. Отличается только наличием опционального ключа 'uri', который позволяет натравливать throttle check только на него.
Пупутно есть несколько настроек:
На github'е есть описание на «английском».
Функционал
- maintenance режим, позволяет выключать view в целом, или отдельные http методы
- глобальные fallback-таймауты сайта для PATCH, POST и т.д.
- локальные таймауты для view в целом, или для отдельных http-методов
Проще показать пример конфига:
DJANGO_THROTTLING = {
'all': 1000,
'post': 'callable:helpers.trash.my_callback',
'congestion': 'forum.views.congestion',
'django.contrib.admin.options.change_view': {
'post': False,
'all': None,
'uri': '/admin/forum/post/23/',
},
}
Остальные примеры с описанием под катом.
Установка
git clone http://github.com/night-crawler/django-throttling.git
cd django-throttling
python setup.py install
Global throttling
Конфиг состоит из секций, самая верхняя — fallback, применяется для всего сайта в целом, в случае, если не нашлось более детализированного правила.
DJANGO_THROTTLING = {
'all': 1000,
'post': 10000,
'congestion': 'forum.views.congestion',
}
В этом примере для запросов всех типов установлено ограничение в 1 запрос в секунду, post — запрос раз в 10 секунд.
congestion может быть uri, или вьюхой. В данном случае будет вызвана вьюха, живущая по соседству:
def congestion(request, congestion_bundle):
user = request.user
progress = int(float(congestion_bundle['delta']) / congestion_bundle['timeout'] * 100)
c = Context({'user': user, 'congestion_bundle': congestion_bundle, 'progress': progress})
return render_to_response(get_theme_template(user, 'congestion.html'), c,
context_instance=RequestContext(request)
)
Это позволит сказать юзеру, что он шалит и, к примеру, должен подождать N секунд. Вьюху можно снабдить скриптом с прогрессбаром, который автоматически зарелоадит форму. В congestion_bundle передается оригинальная вьюха и весь набор ее аргументов, дабы можно было сказать юзеру что-то более детальное.
Можно отключить POST на всем сайте. Тогда юзер увидит HttpResponseBadRequest:
DJANGO_THROTTLING = {
'all': 1000,
'post': False,
'congestion': 'forum.views.congestion',
}
А можно отключить POST вот так, тогда юзер будет отправляться в корень:
DJANGO_THROTTLING = {
'all': 1000,
'post': '/',
'congestion': 'forum.views.congestion',
}
Если простого редиректа недостаточно, то можно сделать свой обработчик для maintenance-mode:
DJANGO_THROTTLING = {
'all': 1000,
'post': 'forum.views.maintenance',
'congestion': 'forum.views.congestion',
}
# forum.views.maintenance
def maintenance(request, maintenance_bundle):
return HttpPreResponse(maintenance_bundle)
Может быть так, что нужно блокировать вьюху только в определенных случаях, например, для блокирования одного топика в форуме, или для еще какого-то частного случая. Тогда можно написать кастомный обработчик для правила:
DJANGO_THROTTLING = {
'all': 1000,
'post': 'callable:helpers.trash.my_callback', # обратите внимание на callable:
'congestion': 'forum.views.congestion',
}
# helpers.trash.my_callback'
def my_callback(request, view_func, view_args, view_kwargs):
return 'some_strange_key_123', 10000
Он должен возвращать кортеж из названия ключа и таймаута. Таймаутом может снова быть int(), False, view, или uri.
Local throttling
В основном имеет аналогичный синтаксис. Отличается только наличием опционального ключа 'uri', который позволяет натравливать throttle check только на него.
DJANGO_THROTTLING = {
'all': 1000,
'post': 'callable:helpers.trash.my_callback',
'congestion': 'forum.views.congestion',
'django.contrib.admin.options.change_view': {
'post': False,
'all': None,
'uri': '/admin/forum/post/23/',
# 'post': 'callable:helpers.trash.my_callback',
# 'all': 4000,
},
}
Пупутно есть несколько настроек:
- DJANGO_THROTTLING_ENABLED: включает троттлинг, по умолчанию выключен.
- DJANGO_THROTTLING_CACHE_EXPIRE: определяет сколько хранятся ключи в кэше. По умолчанию 60*60.
- DJANGO_THROTTLING_CACHE_PREFIX: кэш-префикс, по умолчанию «THROTTLING»
- THROTTLING_CACHE_KEY_PATTERNS: тут хранятся паттерны для генерации ключа. Всего есть 4 разновидности: view_method, view, site_method, site. Можно переопределить.
- DJANGO_THROTTLING_IGNORE_ADMIN: выключает троттлинг, если пользователь — админ. Соответственно, требует подключенного auth
На github'е есть описание на «английском».