Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/branches/cherrypy-2.x/cherrypy/filters/gzipfilter.py

Revision 1519 (checked in by fumanchu, 2 years ago)

2.x backport of [1402]; fix for #577 (GzipFilter doesn't force an update of the Content-Length header)). Also fixes #617.

  • Property svn:eol-style set to native
Line 
1 import struct
2 import time
3 import zlib
4
5 import cherrypy
6 from basefilter import BaseFilter
7
8 class GzipFilter(BaseFilter):
9     """Filter that gzips the response."""
10    
11     def before_finalize(self):
12         if not cherrypy.config.get('gzip_filter.on', False):
13             return
14        
15         response = cherrypy.response
16         if not response.body:
17             # Response body is empty (might be a 304 for instance)
18             return
19        
20         def zipit():
21             # Return a generator that compresses the page
22             varies = response.headers.get("Vary", "")
23             varies = [x.strip() for x in varies.split(",") if x.strip()]
24             if "Accept-Encoding" not in varies:
25                 varies.append("Accept-Encoding")
26             response.headers['Vary'] = ", ".join(varies)
27            
28             response.headers['Content-Encoding'] = 'gzip'
29             level = cherrypy.config.get('gzip_filter.compresslevel', 9)
30             response.body = self.zip_body(response.body, level)
31             # Delete Content-Length header so finalize() recalcs it.
32             response.headers.pop("Content-Length", None)
33        
34         acceptable = cherrypy.request.headers.elements('Accept-Encoding')
35         if not acceptable:
36             # If no Accept-Encoding field is present in a request,
37             # the server MAY assume that the client will accept any
38             # content coding. In this case, if "identity" is one of
39             # the available content-codings, then the server SHOULD use
40             # the "identity" content-coding, unless it has additional
41             # information that a different content-coding is meaningful
42             # to the client.
43             return
44        
45         ct = response.headers.get('Content-Type').split(';')[0]
46         ct = ct in cherrypy.config.get('gzip_filter.mime_types', ['text/html', 'text/plain'])
47         for coding in acceptable:
48             if coding.value == 'identity' and coding.qvalue != 0:
49                 return
50             if coding.value in ('gzip', 'x-gzip'):
51                 if coding.qvalue == 0:
52                     return
53                 if ct:
54                     zipit()
55                 return
56         cherrypy.HTTPError(406, "identity, gzip").set_response()
57    
58     def write_gzip_header(self):
59         """Adapted from the gzip.py standard module code"""
60        
61         header = '\037\213'      # magic header
62         header += '\010'         # compression method
63         header += '\0'
64         header += struct.pack("<L", long(time.time()))
65         header += '\002'
66         header += '\377'
67         return header
68    
69     def write_gzip_trailer(self, crc, size):
70         footer = struct.pack("<l", crc)
71         footer += struct.pack("<L", size & 0xFFFFFFFFL)
72         return footer
73    
74     def zip_body(self, body, compress_level):
75         # Compress page
76         yield self.write_gzip_header()
77         crc = zlib.crc32("")
78         size = 0
79         zobj = zlib.compressobj(compress_level,
80                                 zlib.DEFLATED, -zlib.MAX_WBITS,
81                                 zlib.DEF_MEM_LEVEL, 0)
82         for line in body:
83             size += len(line)
84             crc = zlib.crc32(line, crc)
85             yield zobj.compress(line)
86         yield zobj.flush()
87         yield self.write_gzip_trailer(crc, size)
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets