Here is simple code to reproduce the slowness:
{{{
#!/usr/bin/env python
# Configure needed settings
from django.conf import settings
settings.configure(MESSAGE_TAGS={})
from django.contrib.sessions.middleware import SessionMiddleware
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.messages.storage.cookie import CookieStorage
from django.contrib.messages.api import info
from django.http.request import HttpRequest
from django.http.response import HttpResponse
from django.contrib.messages.storage import default_storage
# Request and response objects
response = HttpResponse()
request = HttpRequest()
# Process request by middleware
SessionMiddleware().process_request(request)
mm = MessageMiddleware()
mm.process_request(request)
# Insert messages
for x in range(500):
info(request, 'm:{0}'.format(x))
# Measure response processing time
import timeit
print(timeit.timeit(
'mm.process_response(request, response)',
globals=globals(), number=10
))
}}}
In my case the DOS was triggered by broken client who repeatedly posted
form generating message, but never did follow redirect to display the
messages, so nothing really sophisticated.
Quickly looking at the code following performance improvements come to my
mind:
* Avoid repeated encoding of the messages, encode them all at once and
then operate on encoded strings
* Avoid calculating HMAC while calculating length as length of it is fixed
* Do bisect instead of removing messages one by one
--
Ticket URL: <https://code.djangoproject.com/ticket/28948>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* cc: Adam (Chainz) Johnson (added)
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:1>
* cc: Sergey Fedoseev (added)
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:2>
* stage: Unreviewed => Accepted
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:3>
* owner: nobody => Srinivas Reddy Thatiparthy
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:4>
* owner: Srinivas Reddy Thatiparthy => (none)
* status: assigned => new
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:5>
* cc: Sergey Fedoseev (removed)
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:6>
* cc: David Wobrock (added)
* owner: nobody => David Wobrock
* has_patch: 0 => 1
* status: new => assigned
Comment:
[https://github.com/django/django/pull/16602 PR]
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:7>
* stage: Accepted => Ready for checkin
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:8>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"9d0c878abf9249da6e16f1acfec311498dc9f368" 9d0c878a]:
{{{
#!CommitTicketReference repository=""
revision="9d0c878abf9249da6e16f1acfec311498dc9f368"
Refs #28948 -- Precomputed once serialized cookie messages.
When the cookie size is too long, the same messages were serialized
over and over again.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:9>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"21757bbdcd6ef31f2a4092fa1bd55dff29214c7a" 21757bbd]:
{{{
#!CommitTicketReference repository=""
revision="21757bbdcd6ef31f2a4092fa1bd55dff29214c7a"
Refs #28948 -- Removed superfluous messages from cookie through bisect.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:10>
* status: assigned => closed
* resolution: => fixed
--
Ticket URL: <https://code.djangoproject.com/ticket/28948#comment:11>