[Zope-dev] Adding gzip compression to HTTPResponse.py

Brad Clements bkc@murkworks.com
Tue, 5 Feb 2002 17:34:26 -0500


I'm looking for architectural suggestions for adding gzip compression to 
HTTPResponse for text types.

First, I just wanted to compress xml-rpc output, since I'm returing lots of table data as 
XML text (not objects), then loading that text/xml into a DOM for XSLT processing.

I hacked the attached code into HTTPResponse, at the end of setBody. It works for 
xml-rpc responses and I suppose any text output, so long as the response object has a 
header named "dogzip" set.

I know "dogzip" is a stupid name, but this is just a testing thing.

Representative compressions:

compress oldlen  150366 new len 11926
compress oldlen  204382 new len 14170
compress oldlen  12746 new len 1364

As you can see, very useful compressions for xml-rpc output.

But for HTML output, what's really needed is I think a special kind of Cache Object. 
One that combines HTTP Caching with Ram caching to keep gzip compressed objects 
"in memory".

Some HTML pages are really quite large, and gzip compression can make a noticable 
difference. Just the javascript code sizes themselves are .. really big.

For xml-rpc, obviously every response must be compressed if it's "worth it", and I can 
see that having to set a response property on a per request basis is appropriate for 
xml-rpc.

But for text file objects, Page Templates and stuff.. How does setBody work with Ram 
Cache objects? I have some ideas...

Anyone think this is worthwhile?

Also, RESPONSE.setBody really should have access to REQUEST.headers. What's 
the clean way to do that? Just pass the request object to response object's init 
method?

Here's quick gzip compression hack-in, based on code posted by Neil Schemenauer

Thanks Neil.

Added about line 265 in HTTPResponse.py in Zope 2.5 B3

        try:
            dogzip = self.headers['dogzip']
            del self.headers['dogzip']
            if dogzip and split(content_type,'/')[0] == 'text':
                body = self.body
                startlen = len(body)
                import zlib, struct
                _gzip_header = ("\037\213" # magic
                                "\010" # compression method
                                "\000" # flags
                                "\000\000\000\000" # time
                                "\002"
                                "\377")
                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)
                print "compress oldlen ",startlen,"new len",newlen
                if newlen < startlen:
                    self.body = z
                    self.setHeader('content-length', newlen)
                    self.setHeader('content-encoding','gzip')
        except:
            pass



Brad Clements,                bkc@murkworks.com   (315)268-1000
http://www.murkworks.com                          (315)268-9812 Fax
netmeeting: ils://ils.murkworks.com               AOL-IM: BKClements