Index: django/http/__init__.py
===================================================================
--- django/http/__init__.py	(revision 7800)
+++ django/http/__init__.py	(working copy)
@@ -268,12 +268,7 @@
         if not content_type:
             content_type = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE,
                     settings.DEFAULT_CHARSET)
-        if not isinstance(content, basestring) and hasattr(content, '__iter__'):
-            self._container = content
-            self._is_string = False
-        else:
-            self._container = [content]
-            self._is_string = True
+        self.content = content
         self.cookies = SimpleCookie()
         if status:
             self.status_code = status
@@ -345,16 +340,29 @@
                         expires='Thu, 01-Jan-1970 00:00:00 GMT')
 
     def _get_content(self):
+        if not self._is_string:
+            self._container = [''.join(self._container)]
+            self._is_string = True
         if self.has_header('Content-Encoding'):
             return ''.join(self._container)
         return smart_str(''.join(self._container), self._charset)
 
     def _set_content(self, value):
-        self._container = [value]
-        self._is_string = True
+        if not isinstance(value, basestring) and hasattr(value, '__iter__'):
+            self._container = value
+            self._is_string = False
+        else:
+            self._container = [value]
+            self._is_string = True
 
     content = property(_get_content, _set_content)
 
+    def _get_content_generator(self):
+        if not self._is_string:
+            return self._container
+
+    content_generator = property(_get_content_generator)
+
     def __iter__(self):
         self._iterator = iter(self._container)
         return self
Index: django/http/utils.py
===================================================================
--- django/http/utils.py	(revision 7800)
+++ django/http/utils.py	(working copy)
@@ -31,3 +31,12 @@
     if request.method == 'HEAD':
         response.content = ''
     return response
+
+def set_content_length(request, response):
+    """
+    Ensures that we always have a Content-Length header, required by some
+    handlers (WSGI), which don't permit chunked transfer encoding.
+    """
+    if not 'Content-Length' in response:
+        response['Content-Length'] = str(len(response.content))
+    return response
Index: django/core/handlers/wsgi.py
===================================================================
--- django/core/handlers/wsgi.py	(revision 7800)
+++ django/core/handlers/wsgi.py	(working copy)
@@ -182,6 +182,9 @@
 class WSGIHandler(BaseHandler):
     initLock = Lock()
     request_class = WSGIRequest
+    response_fixes = [http.fix_location_header,
+                      http.conditional_content_removal,
+                      http.set_content_length]
 
     def __call__(self, environ, start_response):
         from django.conf import settings
Index: django/utils/text.py
===================================================================
--- django/utils/text.py	(revision 7800)
+++ django/utils/text.py	(working copy)
@@ -175,6 +175,18 @@
     zfile.close()
     return zbuf.getvalue()
 
+def compress_sequence(sequence):
+    import cStringIO, gzip
+    zbuf = cStringIO.StringIO()
+    zfile = gzip.GzipFile(mode='wb', compresslevel=6, fileobj=zbuf)
+    yield zbuf.getvalue()
+    for item in sequence:
+        position = zbuf.tell()
+        zfile.write(item)
+        zfile.flush()
+        zbuf.seek(position)
+        yield zbuf.read()
+
 ustring_re = re.compile(u"([\u0080-\uffff])")
 
 def javascript_quote(s, quote_double_quotes=False):
Index: django/middleware/common.py
===================================================================
--- django/middleware/common.py	(revision 7800)
+++ django/middleware/common.py	(working copy)
@@ -107,14 +107,17 @@
         if settings.USE_ETAGS:
             if response.has_header('ETag'):
                 etag = response['ETag']
-            else:
+            elif not response.content_generator:
                 etag = '"%s"' % md5.new(response.content).hexdigest()
-            if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag:
-                cookies = response.cookies
-                response = http.HttpResponseNotModified()
-                response.cookies = cookies
-            else:
-                response['ETag'] = etag
+            try:
+                if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag:
+                    cookies = response.cookies
+                    response = http.HttpResponseNotModified()
+                    response.cookies = cookies
+                else:
+                    response['ETag'] = etag
+            except NameError:
+                pass
 
         return response
 
Index: django/middleware/gzip.py
===================================================================
--- django/middleware/gzip.py	(revision 7800)
+++ django/middleware/gzip.py	(working copy)
@@ -1,6 +1,6 @@
 import re
 
-from django.utils.text import compress_string
+from django.utils.text import compress_sequence, compress_string
 from django.utils.cache import patch_vary_headers
 
 re_accepts_gzip = re.compile(r'\bgzip\b')
@@ -13,7 +13,7 @@
     """
     def process_response(self, request, response):
         # It's not worth compressing non-OK or really short responses.
-        if response.status_code != 200 or len(response.content) < 200:
+        if response.status_code != 200 or (not response.content_generator and len(response.content) < 200):
             return response
 
         patch_vary_headers(response, ('Accept-Encoding',))
@@ -33,7 +33,11 @@
         if not re_accepts_gzip.search(ae):
             return response
 
-        response.content = compress_string(response.content)
+        if response.content_generator:
+            response.content = compress_sequence(response.content_generator)
+            del response['Content-Length']
+        else:
+            response.content = compress_string(response.content)
+            response['Content-Length'] = str(len(response.content))
         response['Content-Encoding'] = 'gzip'
-        response['Content-Length'] = str(len(response.content))
         return response
Index: django/middleware/http.py
===================================================================
--- django/middleware/http.py	(revision 7800)
+++ django/middleware/http.py	(working copy)
@@ -10,7 +10,7 @@
     """
     def process_response(self, request, response):
         response['Date'] = http_date()
-        if not response.has_header('Content-Length'):
+        if not response.has_header('Content-Length') and not response.content_generator:
             response['Content-Length'] = str(len(response.content))
 
         if response.has_header('ETag'):
