How to Use WSGI X-Sendfile¶
To use WSGI X-Sendfile, you don’t only need to integrate it in your application, but you also have to configure your Web server accordingly.
If you have a front-end server and a back-end server, you only need to configure your front-end server. For example, if Nginx is proxying requests to Apache/mod_wsgi, then you only need to configure Nginx.
Integration in Your Application¶
You simply need to initialize XSendfileApplication
and
call its instance from inside your WSGI application. For information on how to
do the latter, please refer to your framework’s documentation.
Example Using Django¶
Django doesn’t offer out-of-the-box support for WSGI applications, so you’d need to use a library like twod.wsgi.
The following code illustrates how to use X-Sendfile from a Django view:
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseNotFound
from twod.wsgi import call_wsgi_app
from xsendfile import XSendfileApplication
DOCUMENT_SENDING_APP = XSendfileApplication("/srv/my-app/uploads/documents")
@login_required
def download_document(request, document_name):
document_path = "/%s.pdf" % document_name
response = call_wsgi_app(DOCUMENT_SENDING_APP, request, document_path)
document_exists = isinstance(response, HttpResponseNotFound)
if document_exists:
response['Content-Disposition'] = "attachment"
return response
Example Using WebOb (Also Applies to Pyramid, Pylons, TurboGears and Others)¶
WebOb offers out-of-the-box support for embedded WSGI applications. The following code illustrates how to use X-Sendfile from a controller powered by WebOb:
from my_framework import login_required
from xsendfile import XSendfileApplication
DOCUMENT_SENDING_APP = XSendfileApplication("/srv/my-app/uploads/documents")
@login_required
def download_document(request, document_name):
download_request = request.copy()
download_request.path_info = "/%s.pdf" % document_name
response = download_request.get_response(DOCUMENT_SENDING_APP)
if response.status_code == 200:
response['Content-Disposition'] = "attachment"
return response
Example With Raw WSGI Applications¶
The following code illustrates how to use X-Sendfile from a WSGI application that isn’t powered by a WSGI framework or library:
from cgi import parse_qs
from xsendfile import XSendfileApplication
DOCUMENT_SENDING_APP = XSendfileApplication("/srv/my-app/uploads/documents")
def application(environ, start_response):
is_user_authenticated = "REMOTE_USER" in environ
if is_user_authenticated:
response_body = download_document(environ, start_response)
else:
response_body = request_authentication(environ, start_response)
return response_body
def download_document(environ, start_response):
new_environ = environ.copy()
new_environ['SCRIPT_NAME'] = environ.get("SCRIPT_NAME", "") + environ['PATH_INFO']
query_string = parse_qs(environ['QUERY_STRING'])
document_path = "/%s.pdf" % query_string.get("document_name")
new_environ['PATH_INFO'] = document_path
response = DOCUMENT_SENDING_APP(new_environ, start_response)
return response
def request_authentication(environ, start_response):
start_response(
"401 WE DON'T KNOW WHO YOU ARE",
[("WWW-Authenticate", 'Basic realm="Document download"')]
)
return []
Integration in Your Front-End Server¶
X-Sendfile is supported out-of-the-box by some servers, such as Lighttpd and Nginx. With other servers, you’d need to install a third party extension.
Please refer to the documentation relevant to your server, or read on if you use Nginx because the process to use X-Sendfile is a little special.
Using Nginx as Front-End Server¶
Nginx’ X-Sendfile support differs a lot from other servers. So if you’re using Nginx as the front-end server, you’d need to change your code slightly to make it work with Nginx.
In Nginx, the feature is called X-Accel, and
it expects the file to be served to be specified in the X-Accel-Redirect
header. However, this file path must be an internal URL path, not a path on disk.
For example, if your uploaded documents are locally stored in
"/srv/my-app/uploads/documents"
, you’d need to have Nginx to make files
in that directory accessible to so-called “internal requests”:
location /internal-document-uploads/ {
internal;
alias /srv/my-app/uploads/documents/;
}
Next, you need to configure XSendfileApplication
to
generate responses that Nginx can interprete:
from xsendfile import NginxSendfile, XSendfileApplication
file_sender = NginxSendfile("/internal-document-uploads/")
DOCUMENT_SENDING_APP = XSendfileApplication(
"/srv/my-app/uploads/documents",
file_sender,
)
You’d then be able to use DOCUMENT_SENDING_APP
as usual.