From 23919b47bd6ec08f0048fd487bbbeff8b20df241 Mon Sep 17 00:00:00 2001 From: Tobias Zanke Date: Tue, 16 May 2017 14:53:52 +0200 Subject: [PATCH 1/4] resources should not return complex responses, also sendfile needs request which is only available in view --- djangodav/fs/resources.py | 46 +++++---------------------------------- djangodav/responses.py | 8 +++++++ djangodav/views/views.py | 31 +++++++++++++++++++++----- 3 files changed, 39 insertions(+), 46 deletions(-) diff --git a/djangodav/fs/resources.py b/djangodav/fs/resources.py index 5584efe..0b02bb7 100644 --- a/djangodav/fs/resources.py +++ b/djangodav/fs/resources.py @@ -18,21 +18,13 @@ # # You should have received a copy of the GNU Affero General Public License # along with DjangoDav. If not, see . -import hashlib -import mimetypes -from sys import getfilesystemencoding -import os import datetime +import os import shutil -import urllib - -from django.http import HttpResponse -from django.utils.http import http_date +from sys import getfilesystemencoding from djangodav.base.resources import BaseDavResource -from djangodav.responses import ResponseException -from djangodav.utils import safe_join, url_join - +from djangodav.utils import url_join fs_encoding = getfilesystemencoding() @@ -43,6 +35,8 @@ class BaseFSDavResource(BaseDavResource): python's os library to do most of the work.""" root = None + quote = False + prefix = "/" def get_abs_path(self): """Return the absolute path of the resource. Used internally to interface with @@ -129,33 +123,3 @@ def write(self, request): class DummyFSDAVResource(DummyReadFSDavResource, DummyWriteFSDavResource, BaseFSDavResource): pass - - -class SendFileFSDavResource(BaseFSDavResource): - quote = False - - def read(self): - response = HttpResponse() - full_path = self.get_abs_path().encode('utf-8') - if self.quote: - full_path = urllib.quote(full_path) - response['X-SendFile'] = full_path - response['Content-Type'] = mimetypes.guess_type(self.displayname) - response['Content-Length'] = self.getcontentlength - response['Last-Modified'] = http_date(self.getlastmodified) - response['ETag'] = self.getetag - raise ResponseException(response) - - -class RedirectFSDavResource(BaseFSDavResource): - prefix = "/" - - def read(self): - response = HttpResponse() - response['X-Accel-Redirect'] = url_join(self.prefix, self.get_path().encode('utf-8')) - response['X-Accel-Charset'] = 'utf-8' - response['Content-Type'] = mimetypes.guess_type(self.displayname) - response['Content-Length'] = self.getcontentlength - response['Last-Modified'] = http_date(self.getlastmodified) - response['ETag'] = self.getetag - raise ResponseException(response) diff --git a/djangodav/responses.py b/djangodav/responses.py index a31e4ed..0b9e529 100644 --- a/djangodav/responses.py +++ b/djangodav/responses.py @@ -41,6 +41,14 @@ def __init__(self, response, *args, **kwargs): self.response = response +class AsSendFileFS(Exception): + pass + + +class RedirectFSException(Exception): + pass + + class HttpResponsePreconditionFailed(HttpResponse): status_code = httplib.PRECONDITION_FAILED diff --git a/djangodav/views/views.py b/djangodav/views/views.py index 7cc46ca..2c9ca9c 100644 --- a/djangodav/views/views.py +++ b/djangodav/views/views.py @@ -6,23 +6,27 @@ from sys import version_info as python_version from django.utils.timezone import now from lxml import etree +import mimetypes +import urllib from django.http import HttpResponseForbidden, HttpResponseNotAllowed, HttpResponseBadRequest, \ - HttpResponseNotModified, HttpResponseRedirect, Http404 + HttpResponseNotModified, HttpResponseRedirect, Http404, HttpResponse from django.utils.decorators import method_decorator from django.utils.functional import cached_property -from django.utils.http import parse_etags +from django.utils.http import parse_etags, http_date from django.shortcuts import render_to_response from django.views.decorators.csrf import csrf_exempt from django.views.generic import View -from djangodav.responses import ResponseException, HttpResponsePreconditionFailed, HttpResponseCreated, HttpResponseNoContent, \ +from djangodav.responses import HttpResponsePreconditionFailed, HttpResponseCreated, HttpResponseNoContent, \ HttpResponseConflict, HttpResponseMediatypeNotSupported, HttpResponseBadGateway, \ - HttpResponseMultiStatus, HttpResponseLocked, HttpResponse + HttpResponseMultiStatus, HttpResponseLocked, RedirectFSException, ResponseException, AsSendFileFS from djangodav.utils import WEBDAV_NSMAP, D, url_join, get_property_tag_list, rfc1123_date from djangodav import VERSION as djangodav_version from django import VERSION as django_version, get_version +from sendfile import sendfile + PATTERN_IF_DELIMITER = re.compile(r'(<([^>]+)>)|(\(([^\)]+)\))') class DavView(View): @@ -190,7 +194,24 @@ def get(self, request, path, head=False, *args, **kwargs): response['ETag'] = self.resource.getetag if not head: response['Content-Length'] = self.resource.getcontentlength - response.content = self.resource.read() + try: + response.content = self.resource.read() + except AsSendFileFS: + assert sendfile is not None, "django-sendfile is not installed." + full_path = self.resource.get_abs_path().encode('utf-8') + if self.resource.quote: + full_path = urllib.quote(full_path) + response = sendfile(request, full_path) + return response + except RedirectFSException: + response = HttpResponse() + response['X-Accel-Redirect'] = url_join(self.resource.prefix, self.resource.get_path().encode('utf-8')) + response['X-Accel-Charset'] = 'utf-8' + response['Content-Type'] = mimetypes.guess_type(self.resource.displayname) + response['Content-Length'] = self.resource.getcontentlength + response['Last-Modified'] = http_date(self.resource.getlastmodified) + response['ETag'] = self.resource.getetag + raise ResponseException(response) elif not head: response = render_to_response(self.template_name, dict(resource=self.resource, base_url=self.base_url)) response['Last-Modified'] = self.resource.getlastmodified From f7357a75a1cc1c25c3dee37820f82aa0117741a6 Mon Sep 17 00:00:00 2001 From: Tobias Zanke Date: Tue, 16 May 2017 16:08:21 +0200 Subject: [PATCH 2/4] try import sendfile --- djangodav/views/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/djangodav/views/views.py b/djangodav/views/views.py index 2c9ca9c..e9c37ee 100644 --- a/djangodav/views/views.py +++ b/djangodav/views/views.py @@ -25,7 +25,10 @@ from djangodav import VERSION as djangodav_version from django import VERSION as django_version, get_version -from sendfile import sendfile +try: + import sendfile +except ImportError: + sendfile = None PATTERN_IF_DELIMITER = re.compile(r'(<([^>]+)>)|(\(([^\)]+)\))') From c03886e5c5aa7f991f0d9c94700ac2fb31697780 Mon Sep 17 00:00:00 2001 From: Tobias Zanke Date: Tue, 16 May 2017 16:17:39 +0200 Subject: [PATCH 3/4] fix sendfile call --- djangodav/views/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangodav/views/views.py b/djangodav/views/views.py index e9c37ee..78dca97 100644 --- a/djangodav/views/views.py +++ b/djangodav/views/views.py @@ -204,7 +204,7 @@ def get(self, request, path, head=False, *args, **kwargs): full_path = self.resource.get_abs_path().encode('utf-8') if self.resource.quote: full_path = urllib.quote(full_path) - response = sendfile(request, full_path) + response = sendfile.sendfile(request, full_path) return response except RedirectFSException: response = HttpResponse() From c01aefd31e3b81b9e053a2c6e331ec1b3935351a Mon Sep 17 00:00:00 2001 From: Tobias Zanke Date: Mon, 19 Jun 2017 14:43:02 +0200 Subject: [PATCH 4/4] remove encode because sendfile does --- djangodav/views/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangodav/views/views.py b/djangodav/views/views.py index 78dca97..672150a 100644 --- a/djangodav/views/views.py +++ b/djangodav/views/views.py @@ -201,7 +201,7 @@ def get(self, request, path, head=False, *args, **kwargs): response.content = self.resource.read() except AsSendFileFS: assert sendfile is not None, "django-sendfile is not installed." - full_path = self.resource.get_abs_path().encode('utf-8') + full_path = self.resource.get_abs_path() if self.resource.quote: full_path = urllib.quote(full_path) response = sendfile.sendfile(request, full_path)