From d69f33ff43f2a0ab8a3751ff6c77ed5f371e9f39 Mon Sep 17 00:00:00 2001 From: Aditya Bansal Date: Thu, 13 Apr 2017 00:05:43 +0530 Subject: [PATCH] backends-nginx: Add support for Nginx versions older than 1.5.9. In this commit we are adding support for Nginx version older than 1.5.9. This is achieved through conditionally quoting the URL's in X-Accel-Redirect headers according to compatibility with Nginx. Since newer versions of Nginx expect URL's in X-Accel-Redirect to be quoted unlike the versions older that Nginx 1.5.9. From this commit onwards we start to expect a 'NGINX_VERSION' config setting to be setup in the django settings in order to facilitate decision making regarding quoting URL's. If such a setting is not found we just assume Nginx version to be greater then 1.5.9 and make decision accordingly and then cache the decision by making use of a decorator. Using this result we decide whether the outgoing URL should be quoted or not. Fixes: #56, #58. Closes #45. --- README.rst | 11 +++++++--- sendfile/backends/_internalredirect.py | 28 +++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 76ccf99..e611e68 100644 --- a/README.rst +++ b/README.rst @@ -11,13 +11,13 @@ The interface is a single function `sendfile(request, filename, attachment=False :: from sendfile import sendfile - + # send myfile.pdf to user return sendfile(request, '/home/john/myfile.pdf') # send myfile.pdf as an attachment (with name myfile.pdf) return sendfile(request, '/home/john/myfile.pdf', attachment=True) - + # send myfile.pdf as an attachment with a different name return sendfile(request, '/home/john/myfile.pdf', attachment=True, attachment_filename='full-name.pdf') @@ -134,8 +134,13 @@ Then the matching location block in nginx.conf would be: You need to pay attention to whether you have trailing slashes or not on the SENDFILE_URL and root values, otherwise you may not get the right URL being sent to NGINX and you may get 404s. You should be able to see what file NGINX is trying to load in the error.log if this happens. From there it should be fairly easy to work out what the right settings are. +Also if you are willing to use django-sendfile with Nginx older than 1.5.9, you need to setup the configuration setting in django settings for specifying Nginx version like this: + +:: + + NGINX_VERSION = '1.4.6' + .. _mod_xsendfile: https://tn123.org/mod_xsendfile/ .. _Apache: http://httpd.apache.org/ .. _Lighthttpd: http://www.lighttpd.net/ .. _mod_wsgi: http://code.google.com/p/modwsgi/ - diff --git a/sendfile/backends/_internalredirect.py b/sendfile/backends/_internalredirect.py index be4e069..b234476 100644 --- a/sendfile/backends/_internalredirect.py +++ b/sendfile/backends/_internalredirect.py @@ -2,6 +2,7 @@ from django.conf import settings from django.utils.encoding import smart_text, smart_bytes +from django.conf import settings try: from urllib.parse import quote @@ -9,6 +10,28 @@ from urllib import quote +def _decision(fn): + _cached_decision = [] + def _decorated(): + if not _cached_decision: + _cached_decision.append(fn()) + return _cached_decision[0] + return _decorated + +@_decision +def should_be_quoted(): + backend = getattr(settings, 'SENDFILE_BACKEND', None) + if backend == 'sendfile.backends.nginx': + nginx_version = getattr(settings, 'NGINX_VERSION', None) + if nginx_version: + nginx_version = map(int, nginx_version.split('.')) + # Since Starting with Nginx 1.5.9, quoted url's are expected to be + # sent with X-Accel-Redirect headers, we will not quote url's for + # versions of Nginx before 1.5.9 + if nginx_version < [1, 5, 9]: + return False + return True + def _convert_file_to_url(filename): relpath = os.path.relpath(filename, settings.SENDFILE_ROOT) @@ -21,4 +44,7 @@ def _convert_file_to_url(filename): # Python3 urllib.parse.quote accepts both unicode and bytes, while Python2 urllib.quote only accepts bytes. # So use bytes for quoting and then go back to unicode. url = [smart_bytes(url_component) for url_component in url] - return smart_text(quote(b'/'.join(url))) + if should_be_quoted(): + return smart_text(quote(b'/'.join(url))) + else: + return smart_text(b'/'.join(url))