瀏覽代碼

[desktop] Backport Django 1.5's ConditionalGetMiddleware to fix streaming bug

This fixes downloading csv files from beeswax.
Erick Tryzelaar 11 年之前
父節點
當前提交
f93ef1e

+ 9 - 0
desktop/core/src/desktop/lib/export_csvxls.py

@@ -88,6 +88,15 @@ def make_response(generator, format, name, encoding=None):
   else:
     raise Exception("Unknown format: %s" % format)
 
+  # FIXME: this should be replaced with StreamingHttpResponse when we upgrade
+  # to Django 1.5+.
   resp = HttpResponse(generator, mimetype=mimetype)
   resp['Content-Disposition'] = 'attachment; filename=%s.%s' % (name, format)
+  resp.streaming = True
+
+  try:
+    del resp['Content-Length']
+  except KeyError:
+    pass
+
   return resp

+ 38 - 0
desktop/core/src/desktop/middleware.py

@@ -638,3 +638,41 @@ class EnsureSafeRedirectURLMiddleware(object):
     else:
       return response
 
+# FIXME: This was backported from Django 1.5 to work around streaming generator
+# responses incorrectly getting a length returned with the response. This can
+# be removed once we upgrade.
+class ConditionalGetMiddleware(object):
+    """
+    Handles conditional GET operations. If the response has a ETag or
+    Last-Modified header, and the request has If-None-Match or
+    If-Modified-Since, the response is replaced by an HttpNotModified.
+
+    Also sets the Date and Content-Length response-headers.
+    """
+    def process_response(self, request, response):
+        from django.utils.http import http_date
+
+        response['Date'] = http_date()
+        if not getattr(response, 'streaming', False) and not response.has_header('Content-Length'):
+            response['Content-Length'] = str(len(response.content))
+
+        if response.has_header('ETag'):
+            if_none_match = request.META.get('HTTP_IF_NONE_MATCH')
+            if if_none_match == response['ETag']:
+                # Setting the status is enough here. The response handling path
+                # automatically removes content for this status code (in
+                # http.conditional_content_removal()).
+                response.status_code = 304
+
+        if response.has_header('Last-Modified'):
+            if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')
+            if if_modified_since is not None:
+                if_modified_since = parse_http_date_safe(if_modified_since)
+            if if_modified_since is not None:
+                last_modified = parse_http_date_safe(response['Last-Modified'])
+                if last_modified is not None and last_modified <= if_modified_since:
+                    # Setting the status code is enough here (same reasons as
+                    # above).
+                    response.status_code = 304
+
+        return response

+ 4 - 1
desktop/core/src/desktop/settings.py

@@ -129,7 +129,10 @@ MIDDLEWARE_CLASSES = [
     'django.middleware.transaction.TransactionMiddleware',
     # 'debug_toolbar.middleware.DebugToolbarMiddleware'
     'django.middleware.csrf.CsrfViewMiddleware',
-    'django.middleware.http.ConditionalGetMiddleware',
+
+    # FIXME: we can switch back to the django version when we upgrade to 1.5+
+    #'django.middleware.http.ConditionalGetMiddleware',
+    'desktop.middleware.ConditionalGetMiddleware',
 ]
 
 if os.environ.get(ENV_DESKTOP_DEBUG):