-
-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refs #1 - Introduced experimental support for Nginx X-Accel. WORK IN …
…PROGRESS.
- Loading branch information
1 parent
46542cd
commit 41e00d3
Showing
4 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
"""Let Nginx serve files for increased performance. | ||
See `Nginx X-accel documentation <http://wiki.nginx.org/X-accel>`_. | ||
""" | ||
from datetime import datetime, timedelta | ||
|
||
from django.conf import settings | ||
from django.http import HttpResponse | ||
|
||
from django_downloadview.middlewares import BaseDownloadMiddleware | ||
from django_downloadview.decorators import DownloadDecorator | ||
|
||
|
||
#: Default value for X-Accel-Buffering header. | ||
DEFAULT_BUFFERING = None | ||
if not hasattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_BUFFERING'): | ||
setattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_BUFFERING', DEFAULT_BUFFERING) | ||
|
||
|
||
#: Default value for X-Accel-Limit-Rate header. | ||
DEFAULT_LIMIT_RATE = None | ||
if not hasattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE'): | ||
setattr(settings, 'NGINX_DOWNLOAD_MIDDLEWARE_LIMIT', DEFAULT_LIMIT_RATE) | ||
|
||
|
||
def content_type_to_charset(content_type): | ||
return 'utf-8' | ||
|
||
|
||
class XAccelRedirectResponse(HttpResponse): | ||
"""Http response that delegate serving file to Nginx.""" | ||
def __init__(self, url, content_type, basename=None, expires=None, | ||
with_buffering=None, limit_rate=None): | ||
"""Return a HttpResponse with headers for Nginx X-Accel-Redirect.""" | ||
super(XAccelRedirectResponse, self).__init__(content_type=content_type) | ||
basename = basename or url.split('/')[-1] | ||
self['Content-Disposition'] = 'attachment; filename=%s' % basename | ||
self['X-Accel-Redirect'] = url | ||
self['X-Accel-Charset'] = content_type_to_charset(content_type) | ||
if with_buffering is not None: | ||
self['X-Accel-Buffering'] = with_buffering and 'yes' or 'no' | ||
if expires: | ||
expire_seconds = timedelta(expires - datetime.now()).seconds | ||
self['X-Accel-Expires'] = expire_seconds | ||
elif expires is not None: # We explicitely want it off. | ||
self['X-Accel-Expires'] = 'off' | ||
if limit_rate is not None: | ||
self['X-Accel-Limit-Rate'] = limit_rate and '%d' % limit_rate \ | ||
or 'off' | ||
|
||
|
||
class BaseXAccelRedirectMiddleware(BaseDownloadMiddleware): | ||
"""Looks like a middleware, but configurable.""" | ||
def __init__(self, expires=None, with_buffering=None, limit_rate=None): | ||
"""Constructor.""" | ||
self.expires = expires | ||
self.with_buffering = with_buffering | ||
self.limit_rate = limit_rate | ||
|
||
def file_to_url(response): | ||
return response.filename | ||
|
||
def process_download_response(self, request, response): | ||
"""Replace DownloadResponse instances by NginxDownloadResponse ones.""" | ||
url = self.file_to_url(response) | ||
if self.expires: | ||
expires = self.expires | ||
else: | ||
try: | ||
expires = response.expires | ||
except AttributeError: | ||
expires = None | ||
return XAccelRedirectResponse(url=url, | ||
content_type=response.content_type, | ||
basename=response.basename, | ||
expires=expires, | ||
with_buffering=self.with_buffering, | ||
limit_rate=self.limit_rate) | ||
|
||
|
||
class XAccelRedirectMiddleware(): | ||
"""Apply X-Accel-Redirect globally. | ||
XAccelRedirectResponseHandler with django settings. | ||
""" | ||
def __init__(self): | ||
"""Use Django settings as configuration.""" | ||
super(XAccelRedirectMiddleware, self).__init__( | ||
expires=settings.NGINX_DOWNLOAD_MIDDLEWARE_EXPIRESS, | ||
with_buffering=settings.NGINX_DOWNLOAD_MIDDLEWARE_WITH_BUFFERING, | ||
limit_rate=settings.NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE) | ||
|
||
|
||
#: Apply BaseXAccelRedirectMiddleware to ``view_func`` response. | ||
#: | ||
#: Proxies additional arguments (``*args``, ``**kwargs``) to | ||
#: :py:meth:`django_downloadview.nginx.BaseXAccelRedirectMiddleware.__init__`: | ||
#: ``expires``, ``with_buffering``, and ``limit_rate``. | ||
x_accel_redirect = DownloadDecorator(BaseXAccelRedirectMiddleware) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ Contents | |
.. toctree:: | ||
:maxdepth: 2 | ||
|
||
nginx | ||
api/modules | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
################### | ||
Nginx optimisations | ||
################### | ||
|
||
If you serve Django behind Nginx, then you can delegate the file download | ||
service to Nginx and get increased performance: | ||
|
||
* lower resources used by Python/Django workers ; | ||
* faster download. | ||
|
||
See `Nginx X-accel documentation`_ for details. | ||
|
||
|
||
**************************** | ||
Configure some download view | ||
**************************** | ||
|
||
As an example, let's consider the following download view: | ||
|
||
* mapped on ``/document/<object-slug>/download`` | ||
* returns DownloadResponse corresponding to Document's model FileField | ||
* Document storage root is :file:`/var/www/files/`. | ||
|
||
Configure Document storage: | ||
|
||
.. code-block:: python | ||
|
||
storage = FileSystemStorage(location='var/www/files', | ||
url='/optimized-download') | ||
|
||
As is, Django is to serve the files, i.e. load chunks into memory and stream | ||
them. | ||
|
||
Nginx is much more efficient for the actual streaming. | ||
|
||
|
||
*************** | ||
Configure Nginx | ||
*************** | ||
|
||
See `Nginx X-accel documentation`_ for details. | ||
|
||
In this documentation, let's suppose we have something like this: | ||
|
||
.. code-block:: nginx | ||
|
||
# Will serve /var/www/files/myfile.tar.gz | ||
# When passed URI /protected_files/myfile.tar.gz | ||
location /optimized-download { | ||
internal; | ||
alias /var/www/files; | ||
} | ||
|
||
.. note:: | ||
|
||
``/optimized-download`` is not available for the client, i.e. users | ||
won't be able to download files via ``/optimized-download/<filename>``. | ||
|
||
.. warning:: | ||
|
||
Make sure Nginx can read the files to download! Check permissions. | ||
|
||
|
||
************************************************ | ||
Global delegation, with XAccelRedirectMiddleware | ||
************************************************ | ||
|
||
If you want to delegate all file downloads to Nginx, then use | ||
:py:class:`django_downloadview.nginx.XAccelRedirectMiddleware`. | ||
|
||
Register it in your settings: | ||
|
||
.. code-block:: python | ||
|
||
MIDDLEWARE_CLASSES = ( | ||
# ... | ||
'django_downloadview.nginx.XAccelRedirectMiddleware', | ||
# ... | ||
) | ||
|
||
Optionally customize configuration (default is "use Nginx's defaults"). | ||
|
||
.. code-block:: python | ||
|
||
NGINX_DOWNLOAD_MIDDLEWARE_EXPIRES = False # Force no expiration. | ||
NGINX_DOWNLOAD_MIDDLEWARE_WITH_BUFFERING = False # Force buffering off. | ||
NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE = False # Force limit rate off. | ||
|
||
|
||
************************************************* | ||
Local delegation, with x_accel_redirect decorator | ||
************************************************* | ||
|
||
If you want to delegate file downloads to Nginx on a per-view basis, then use | ||
:py:func:`django_downloadview.nginx.x_accel_redirect` decorator. | ||
|
||
In some urls.py: | ||
|
||
.. code-block:: python | ||
|
||
# ... import Document and django.core.urls | ||
|
||
from django_downloadview import ObjectDownloadView | ||
from django_downloadview.nginx import x_accel_redirect | ||
|
||
|
||
download = x_accel_redirect(ObjectDownloadView.as_view(model=Document)) | ||
|
||
# ... URL patterns using ``download`` | ||
|
||
|
||
********** | ||
References | ||
********** | ||
|
||
.. target-notes:: | ||
|
||
.. _`Nginx X-accel documentation`: http://wiki.nginx.org/X-accel |