From e6d68c73b82a06ff1fdda7d3d4819b194311e1db Mon Sep 17 00:00:00 2001 From: Tanner Prestegard <tanner.prestegard@ligo.org> Date: Thu, 12 Jul 2018 12:44:51 -0500 Subject: [PATCH] adding utilities for serving files --- gracedb/core/http.py | 63 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 gracedb/core/http.py diff --git a/gracedb/core/http.py b/gracedb/core/http.py new file mode 100644 index 000000000..2285139ef --- /dev/null +++ b/gracedb/core/http.py @@ -0,0 +1,63 @@ +# Request/response utilities +from django.http import HttpResponse + +from .vfile import VersionedFile + +import os + + +def serve_file(file_path, ResponseClass=HttpResponse): + """ + Take an absolute path to a file and construct a response. + Files are served by Apache through X-Sendfile. + + If a certain response class is desired, it can be passed as an argument. + Typically will be django.http.HttpResponse or + rest_framework.response.Response. + + This function does NOT check that the file exists and is readable, + or whether the user should be allowed to download the file. + """ + + # Try to guess file content type; if unknown, set as octet-stream + content_type, encoding = VersionedFile.guess_mimetype(file_path) + content_type = content_type or "application/octet-stream" + + # Set up response object + response = ResponseClass() + + # Configure response to have Apache serve the file with X-Sendfile + response['X-Sendfile'] = file_path + + # For binary files, add the file as an attachment (direct download instead + # of opening in browser window) + if content_type == "application/octet-stream": + response['Content-Disposition'] = 'attachment; filename="{0}"'.format( + os.path.basename(file_path)) + + return response + + +def check_and_serve_file(request, file_path, ResponseClass=HttpResponse): + """ + Checks whether a file exists and is readable. If so, the file is served. + Does not check permissions - that should be done before this function + is called. + + This function returns a response, so it should be called within a view, + not from within a view subfunction or method. + """ + + if not os.path.exists(file_path): + err_msg = "File {0} not found".format(os.path.basename(file_path)) + # File not found - return 404 NOT FOUND response + response = ResponseClass(err_msg, status=404) + elif not os.access(file_path, os.R_OK): + err_msg = "File {0} is not readable".format( + os.path.basename(file_path)) + # File not readable - return 500 SERVER ERROR response + response = ResponseClass(err_msg, status=500) + elif os.path.isfile(file_path): + response = serve_file(file_path, ResponseClass) + + return response -- GitLab