1 from __future__
import with_statement
2 import zlib
, cStringIO
, os
, struct
, hashlib
5 from fcgi
.fcgiutil
import flatten
7 def gzip(data
, cache_file
=None):
8 """ Compresses a string, returns the compressed bytes.
9 If cache_file is specified, uses that file as a cache.
13 using_temp_file
= False
14 if cache_file
is None:
15 cache_file
= os
.path
.sep
.join([Settings
.cacheDir
,"tmp_"+hashlib
.sha1(os
.urandom(1024)).hexdigest()])
16 # log("Using temp file: %s" % cache_file)
17 using_temp_file
= True
18 if not os
.path
.isfile(cache_file
):
19 # log("writing %d bytes to cache file: %s" % (len(data), cache_file))
20 with
open(cache_file
, "wb") as fo
:
21 fz
= _gzip
.GzipFile(fileobj
=fo
, mode
="wb", compresslevel
=9)
26 with
open(cache_file
, "rb") as fi
:
28 # log("gzip: read %d bytes" % len(data))
30 # log("Deleting temp file: %s" % cache_file)
35 # log("gzip: %.2f ms" % ms)
37 def manual_gzip(data
, cache_file
=None):
38 _header
= ("\037\213\010\000" # magic, type, flags
39 "\000\000\000\000" # mtime
41 co
= zlib
.compressobj()
42 return ''.join([_header
, co
.compress(data
)[2:], co
.flush()])
43 # struct.pack("<ll",zlib.crc32(data),len(data))])
45 def generate_file(file, block_size
=4096):
46 """ Returns a generator that reads a file in blocks, for streaming large files. """
47 with
open(file, "rb") as f
:
49 data
= f
.read(block_size
)
50 if data
is None or len(data
) == 0:
54 def generate_gzip(input, cache_file
=None):
56 buf
= cStringIO
.StringIO()
58 for text
in flatten(input):
59 if text
not in (None, ''):
60 if not pass_through
and len(text
) >= 2:
61 if str(text
[:2]) == header
:
65 # log("Detected an already compressed stream, falling into pass-through mode.")
68 yield gzip(buf
.getvalue(), cache_file
=cache_file
)
70 def generate_deflate(input, cache_file
=None):
71 # TODO: pass-through mode
72 buf
= cStringIO
.StringIO()
73 for text
in flatten(input):
74 if text
not in (None, ''):
76 yield deflate(buf
.getvalue(), cache_file
=cache_file
)
79 def __disabled__generate_gzip(input, cache_file
=None):
80 """ Returns a generator that reads from the supplied generator,
81 and yields it's output, compressed.
83 # log("generating gzip (cache_file: %s)" % cache_file)
84 if cache_file
is not None and os
.path
.isfile(cache_file
):
85 # log("found existing cache file: os.path.isfile('%s') == %s" % (cache_file, os.path.isfile(cache_file)))
86 yield generate_file(cache_file
)
88 # log("no cache file, generating a stream, teeing the output to a cachefile at: %s" % cache_file)
89 co
= zlib
.compressobj()
90 # magic, type, flags + mtime + xfl, os
91 header
= "\037\213\010\000"+"\000\000\000\000"+ "\002\377"
93 # the compress module produces 2 extra bytes in the header that are not supported by browsers
94 skim_first_bytes
= True
95 # keep track of the checksum of the data we send out
98 # if this stream is already compressed we will fall into pass_through mode
100 cache_file
= open(cache_file
, "wb") if cache_file
is not None else None
102 for text
in flatten(input):
103 if text
is not None and len(text
) > 0:
107 if len(text
) >= 4 and text
[:4] == header
[:4]: # detect a compression header
108 # log("Detected already compressed stream, flipping to pass trhough mode.")
112 # keep a rolling checksum of all data sent
113 checksum
= zlib
.crc32(text
, checksum
)
114 len_data
+= len(text
)
115 ztext
= co
.compress(text
)
118 yield _tee(cache_file
, header
)
121 skim_first_bytes
= False
122 yield _tee(cache_file
, ztext
[2:])
124 yield _tee(cache_file
, ztext
)
125 yield _tee(cache_file
, co
.flush())
126 yield _tee(cache_file
, struct
.pack("<l",checksum
))
128 if cache_file
is not None:
136 def deflate(data
, cache_file
=None):
137 using_temp_file
= False
138 if cache_file
is None:
139 cache_file
= os
.path
.sep
.join([Settings
.cacheDir
,"tmp_"+hashlib
.sha1(os
.urandom(1024)).hexdigest()])
140 using_temp_file
= True
141 if not os
.path
.isfile(cache_file
):
142 with
open(cache_file
, "wb") as fo
:
143 data
= cStringIO
.StringIO(zlib
.compress(data
)).read()
144 # log("writing deflate file: %d bytes" % len(data))
147 with
open(cache_file
, "rb") as fi
:
149 # log("deflate: read %d bytes" % len(data))
151 os
.remove(cache_file
)