1 import struct
2 import time
3
4 import cherrypy
5
6
7 -def decode(encoding=None, default_encoding='utf-8'):
33
49
50
51
52
53 -def encode(encoding=None, errors='strict', text_only=True, add_charset=True):
67
69 """Encode a streaming response body.
70
71 Use a generator wrapper, and just pray it works as the stream is
72 being written out.
73 """
74 def encoder(body):
75 for chunk in body:
76 if isinstance(chunk, unicode):
77 chunk = chunk.encode(encoding, errors)
78 yield chunk
79 cherrypy.response.body = encoder(cherrypy.response.body)
80 return True
81
95
97 response = cherrypy.response
98
99 if cherrypy.response.stream:
100 encoder = encode_stream
101 else:
102 response.collapse_body()
103 encoder = encode_string
104 if response.headers.has_key("Content-Length"):
105
106
107
108
109
110
111
112
113
114
115 del response.headers["Content-Length"]
116
117
118
119 encs = cherrypy.request.headers.elements('Accept-Charset')
120 charsets = [enc.value.lower() for enc in encs]
121 attempted_charsets = []
122
123 if encoding is not None:
124
125 encoding = encoding.lower()
126 if (not charsets) or "*" in charsets or encoding in charsets:
127 if encoder(encoding, errors):
128 return encoding
129 else:
130 if not encs:
131
132 if encoder(default_encoding, errors):
133 return default_encoding
134 else:
135 raise cherrypy.HTTPError(500, failmsg % default_encoding)
136 else:
137 if "*" not in charsets:
138
139
140
141
142 iso = 'iso-8859-1'
143 if iso not in charsets:
144 attempted_charsets.append(iso)
145 if encoder(iso, errors):
146 return iso
147
148 for element in encs:
149 if element.qvalue > 0:
150 if element.value == "*":
151
152 if default_encoding not in attempted_charsets:
153 attempted_charsets.append(default_encoding)
154 if encoder(default_encoding, errors):
155 return default_encoding
156 else:
157 encoding = element.value
158 if encoding not in attempted_charsets:
159 attempted_charsets.append(encoding)
160 if encoder(encoding, errors):
161 return encoding
162
163
164 ac = cherrypy.request.headers.get('Accept-Charset')
165 if ac is None:
166 msg = "Your client did not send an Accept-Charset header."
167 else:
168 msg = "Your client sent this Accept-Charset header: %s." % ac
169 msg += " We tried these charsets: %s." % ", ".join(attempted_charsets)
170 raise cherrypy.HTTPError(406, msg)
171
172
173
174
176 """Compress 'body' at the given compress_level."""
177 import zlib
178
179 yield '\037\213'
180 yield '\010'
181 yield '\0'
182 yield struct.pack("<L", long(time.time()))
183 yield '\002'
184 yield '\377'
185
186 crc = zlib.crc32("")
187 size = 0
188 zobj = zlib.compressobj(compress_level,
189 zlib.DEFLATED, -zlib.MAX_WBITS,
190 zlib.DEF_MEM_LEVEL, 0)
191 for line in body:
192 size += len(line)
193 crc = zlib.crc32(line, crc)
194 yield zobj.compress(line)
195 yield zobj.flush()
196 yield struct.pack("<l", crc)
197 yield struct.pack("<L", size & 0xFFFFFFFFL)
198
200 import gzip, StringIO
201
202 zbuf = StringIO.StringIO()
203 zbuf.write(body)
204 zbuf.seek(0)
205 zfile = gzip.GzipFile(mode='rb', fileobj=zbuf)
206 data = zfile.read()
207 zfile.close()
208 return data
209
210
211 -def gzip(compress_level=9, mime_types=['text/html', 'text/plain']):
212 """Try to gzip the response body if Content-Type in mime_types.
213
214 cherrypy.response.headers['Content-Type'] must be set to one of the
215 values in the mime_types arg before calling this function.
216
217 No compression is performed if any of the following hold:
218 * The client sends no Accept-Encoding request header
219 * No 'gzip' or 'x-gzip' is present in the Accept-Encoding header
220 * No 'gzip' or 'x-gzip' with a qvalue > 0 is present
221 * The 'identity' value is given with a qvalue > 0.
222 """
223 response = cherrypy.response
224 if not response.body:
225
226 return
227
228
229
230 if getattr(cherrypy.request, "cached", False):
231 return
232
233 acceptable = cherrypy.request.headers.elements('Accept-Encoding')
234 if not acceptable:
235
236
237
238
239
240
241
242 return
243
244 ct = response.headers.get('Content-Type', '').split(';')[0]
245 for coding in acceptable:
246 if coding.value == 'identity' and coding.qvalue != 0:
247 return
248 if coding.value in ('gzip', 'x-gzip'):
249 if coding.qvalue == 0:
250 return
251 if ct in mime_types:
252
253 varies = response.headers.get("Vary", "")
254 varies = [x.strip() for x in varies.split(",") if x.strip()]
255 if "Accept-Encoding" not in varies:
256 varies.append("Accept-Encoding")
257 response.headers['Vary'] = ", ".join(varies)
258
259 response.headers['Content-Encoding'] = 'gzip'
260 response.body = compress(response.body, compress_level)
261 if response.headers.has_key("Content-Length"):
262
263 del response.headers["Content-Length"]
264 return
265 cherrypy.HTTPError(406, "identity, gzip").set_response()
266