[Zope-Checkins] CVS: Zope/lib/python/ZPublisher - HTTPResponse.py:1.62

Brian Lloyd brian@zope.com
Wed, 12 Jun 2002 17:51:39 -0400


Update of /cvs-repository/Zope/lib/python/ZPublisher
In directory cvs.zope.org:/tmp/cvs-serv26166

Modified Files:
	HTTPResponse.py 
Log Message:
Merged Brad Clements' gzip encoding patch.


=== Zope/lib/python/ZPublisher/HTTPResponse.py 1.61 => 1.62 ===
 
 import types, os, sys, re
+import zlib, struct
 from string import translate, maketrans
 from types import StringType, InstanceType, LongType, UnicodeType
 from BaseResponse import BaseResponse
@@ -100,6 +101,24 @@
 accumulate_header={'set-cookie': 1}.has_key
 
 
+_gzip_header = ("\037\213" # magic
+                "\010" # compression method
+                "\000" # flags
+                "\000\000\000\000" # time
+                "\002"
+                "\377")
+
+uncompressableMimeMajorTypes = ('image',)   # these mime major types should
+                                            # not be gzip content encoded
+
+# The environment variable DONT_GZIP_MAJOR_MIME_TYPES can be set to a list
+# of comma seperated mime major types which should also not be compressed
+
+otherTypes = os.environ.get('DONT_GZIP_MAJOR_MIME_TYPES','').lower()
+if otherTypes:
+    uncompressableMimeMajorTypes += tuple(otherTypes.split(','))
+
+
 class HTTPResponse(BaseResponse):
     """\
     An object representation of an HTTP response.
@@ -126,6 +145,7 @@
     realm='Zope'
     _error_format='text/html'
     _locked_status = 0
+    use_HTTP_content_compression = 0    # indicate if setBody should content-compress output
 
     def __init__(self,body='',status=200,headers=None,
                  stdout=sys.stdout, stderr=sys.stderr,):
@@ -292,7 +312,75 @@
 
         self.setHeader('content-length', len(self.body))
         self.insertBase()
+        if self.use_HTTP_content_compression and \
+            not self.headers.get('content-encoding',None):
+            # use HTTP content encoding to compress body contents unless 
+            # this response already has another type of content encoding
+            if content_type.split('/')[0] not in uncompressableMimeMajorTypes:
+                # only compress if not listed as uncompressable
+                body = self.body
+                startlen = len(body)
+                co = zlib.compressobj(6,zlib.DEFLATED,-zlib.MAX_WBITS,
+                                      zlib.DEF_MEM_LEVEL,0)
+                chunks = [_gzip_header, co.compress(body),
+                          co.flush(),
+                          struct.pack("<ll",zlib.crc32(body),startlen)]
+                z = "".join(chunks)
+                newlen = len(z)
+                if newlen < startlen:
+                    self.body = z
+                    self.setHeader('content-length', newlen)
+                    self.setHeader('content-encoding','gzip')
+            
         return self
+
+    def enableHTTPCompression(self,REQUEST={},force=0,disable=0,query=0):
+        """Enable HTTP Content Encoding with gzip compression if possible
+
+           REQUEST -- used to check if client can accept compression
+           force   -- set true to ignore REQUEST headers
+           disable -- set true to disable compression
+           query   -- just return if compression has been previously requested
+
+           returns -- 1 if compression will be performed, 0 otherwise
+
+           The HTTP specification allows for transfer encoding and content
+           encoding. Unfortunately many web browsers still do not support
+           transfer encoding, but they all seem to support content encoding.
+
+           This function is designed to be called on each request to specify
+           on a request-by-request basis that the response content should
+           be compressed. This is quite useful for xml-rpc transactions, where
+           compression rates of 90% or more can be achieved for text data.
+
+           The REQUEST headers are used to determine if the client accepts
+           gzip content encoding. The force parameter can force the use
+           of gzip encoding regardless of REQUEST, and the disable parameter
+           can be used to "turn off" previously enabled encoding (but note
+           that any existing content-encoding header will not be changed).
+           The query parameter can be used to determine the if compression
+           has been previously requested.
+
+           In setBody, the major mime type is used to determine if content
+           encoding should actually be performed.
+
+           By default, image types are not compressed.
+           Additional major mime types can be specified by setting
+           the environment variable DONT_GZIP_MAJOR_MIME_TYPES to a comma-seperated
+           list of major mime types that should also not be gzip compressed.
+        """
+        if query:
+            return self.use_HTTP_content_compression
+
+        elif disable:
+            # in the future, a gzip cache manager will need to ensure that compression is off
+            self.use_HTTP_content_compression = 0
+
+        elif force or (REQUEST.get('HTTP_ACCEPT_ENCODING','').find('gzip') != -1):
+            self.use_HTTP_content_compression = 1
+            
+            
+        return self.use_HTTP_content_compression
 
     def _encode_unicode(self,body,charset_re=re.compile(r'text/[0-9a-z]+\s*;\s*charset=([-_0-9a-z]+)(?:(?:\s*;)|\Z)',re.IGNORECASE)):
         # Encode the Unicode data as requested