encoding.py (10466B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\lib\encoding.py 2 import struct 3 import time 4 import cherrypy 5 from cherrypy._cpcompat import basestring, BytesIO, ntob, set, unicodestr 6 from cherrypy.lib import file_generator 7 from cherrypy.lib import set_vary_header 8 9 def decode(encoding = None, default_encoding = 'utf-8'): 10 body = cherrypy.request.body 11 if encoding is not None: 12 if not isinstance(encoding, list): 13 encoding = [encoding] 14 body.attempt_charsets = encoding 15 elif default_encoding: 16 if not isinstance(default_encoding, list): 17 default_encoding = [default_encoding] 18 body.attempt_charsets = body.attempt_charsets + default_encoding 19 20 21 class ResponseEncoder: 22 default_encoding = 'utf-8' 23 failmsg = 'Response body could not be encoded with %r.' 24 encoding = None 25 errors = 'strict' 26 text_only = True 27 add_charset = True 28 debug = False 29 30 def __init__(self, **kwargs): 31 for k, v in kwargs.items(): 32 setattr(self, k, v) 33 34 self.attempted_charsets = set() 35 request = cherrypy.serving.request 36 if request.handler is not None: 37 if self.debug: 38 cherrypy.log('Replacing request.handler', 'TOOLS.ENCODE') 39 self.oldhandler = request.handler 40 request.handler = self 41 42 def encode_stream(self, encoding): 43 if encoding in self.attempted_charsets: 44 return False 45 self.attempted_charsets.add(encoding) 46 47 def encoder(body): 48 for chunk in body: 49 if isinstance(chunk, unicodestr): 50 chunk = chunk.encode(encoding, self.errors) 51 yield chunk 52 53 self.body = encoder(self.body) 54 return True 55 56 def encode_string(self, encoding): 57 if encoding in self.attempted_charsets: 58 return False 59 else: 60 self.attempted_charsets.add(encoding) 61 try: 62 body = [] 63 for chunk in self.body: 64 if isinstance(chunk, unicodestr): 65 chunk = chunk.encode(encoding, self.errors) 66 body.append(chunk) 67 68 self.body = body 69 except (LookupError, UnicodeError): 70 return False 71 72 return True 73 74 def find_acceptable_charset(self): 75 request = cherrypy.serving.request 76 response = cherrypy.serving.response 77 if self.debug: 78 cherrypy.log('response.stream %r' % response.stream, 'TOOLS.ENCODE') 79 if response.stream: 80 encoder = self.encode_stream 81 else: 82 encoder = self.encode_string 83 if 'Content-Length' in response.headers: 84 del response.headers['Content-Length'] 85 encs = request.headers.elements('Accept-Charset') 86 charsets = [ enc.value.lower() for enc in encs ] 87 if self.debug: 88 cherrypy.log('charsets %s' % repr(charsets), 'TOOLS.ENCODE') 89 if self.encoding is not None: 90 encoding = self.encoding.lower() 91 if self.debug: 92 cherrypy.log('Specified encoding %r' % encoding, 'TOOLS.ENCODE') 93 if not charsets or '*' in charsets or encoding in charsets: 94 if self.debug: 95 cherrypy.log('Attempting encoding %r' % encoding, 'TOOLS.ENCODE') 96 if encoder(encoding): 97 return encoding 98 elif not encs: 99 if self.debug: 100 cherrypy.log('Attempting default encoding %r' % self.default_encoding, 'TOOLS.ENCODE') 101 if encoder(self.default_encoding): 102 return self.default_encoding 103 raise cherrypy.HTTPError(500, self.failmsg % self.default_encoding) 104 else: 105 for element in encs: 106 if element.qvalue > 0: 107 if element.value == '*': 108 if self.debug: 109 cherrypy.log('Attempting default encoding due to %r' % element, 'TOOLS.ENCODE') 110 if encoder(self.default_encoding): 111 return self.default_encoding 112 else: 113 encoding = element.value 114 if self.debug: 115 cherrypy.log('Attempting encoding %s (qvalue >0)' % element, 'TOOLS.ENCODE') 116 if encoder(encoding): 117 return encoding 118 119 if '*' not in charsets: 120 iso = 'iso-8859-1' 121 if iso not in charsets: 122 if self.debug: 123 cherrypy.log('Attempting ISO-8859-1 encoding', 'TOOLS.ENCODE') 124 if encoder(iso): 125 return iso 126 ac = request.headers.get('Accept-Charset') 127 if ac is None: 128 msg = 'Your client did not send an Accept-Charset header.' 129 else: 130 msg = 'Your client sent this Accept-Charset header: %s.' % ac 131 msg += ' We tried these charsets: %s.' % ', '.join(self.attempted_charsets) 132 raise cherrypy.HTTPError(406, msg) 133 134 def __call__(self, *args, **kwargs): 135 response = cherrypy.serving.response 136 self.body = self.oldhandler(*args, **kwargs) 137 if isinstance(self.body, basestring): 138 if self.body: 139 self.body = [self.body] 140 else: 141 self.body = [] 142 elif hasattr(self.body, 'read'): 143 self.body = file_generator(self.body) 144 elif self.body is None: 145 self.body = [] 146 ct = response.headers.elements('Content-Type') 147 if self.debug: 148 cherrypy.log('Content-Type: %r' % [ str(h) for h in ct ], 'TOOLS.ENCODE') 149 if ct: 150 ct = ct[0] 151 if self.text_only: 152 if ct.value.lower().startswith('text/'): 153 if self.debug: 154 cherrypy.log('Content-Type %s starts with "text/"' % ct, 'TOOLS.ENCODE') 155 do_find = True 156 else: 157 if self.debug: 158 cherrypy.log('Not finding because Content-Type %s does not start with "text/"' % ct, 'TOOLS.ENCODE') 159 do_find = False 160 else: 161 if self.debug: 162 cherrypy.log('Finding because not text_only', 'TOOLS.ENCODE') 163 do_find = True 164 if do_find: 165 ct.params['charset'] = self.find_acceptable_charset() 166 if self.add_charset: 167 if self.debug: 168 cherrypy.log('Setting Content-Type %s' % ct, 'TOOLS.ENCODE') 169 response.headers['Content-Type'] = str(ct) 170 return self.body 171 172 173 def compress(body, compress_level): 174 import zlib 175 yield ntob('\x1f\x8b') 176 yield ntob('\x08') 177 yield ntob('\x00') 178 yield struct.pack('<L', int(time.time()) & int('FFFFFFFF', 16)) 179 yield ntob('\x02') 180 yield ntob('\xff') 181 crc = zlib.crc32(ntob('')) 182 size = 0 183 zobj = zlib.compressobj(compress_level, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0) 184 for line in body: 185 size += len(line) 186 crc = zlib.crc32(line, crc) 187 yield zobj.compress(line) 188 189 yield zobj.flush() 190 yield struct.pack('<L', crc & int('FFFFFFFF', 16)) 191 yield struct.pack('<L', size & int('FFFFFFFF', 16)) 192 193 194 def decompress(body): 195 import gzip 196 zbuf = BytesIO() 197 zbuf.write(body) 198 zbuf.seek(0) 199 zfile = gzip.GzipFile(mode='rb', fileobj=zbuf) 200 data = zfile.read() 201 zfile.close() 202 return data 203 204 205 def gzip(compress_level = 5, mime_types = ['text/html', 'text/plain'], debug = False): 206 request = cherrypy.serving.request 207 response = cherrypy.serving.response 208 set_vary_header(response, 'Accept-Encoding') 209 if not response.body: 210 if debug: 211 cherrypy.log('No response body', context='TOOLS.GZIP') 212 return 213 if getattr(request, 'cached', False): 214 if debug: 215 cherrypy.log('Not gzipping cached response', context='TOOLS.GZIP') 216 return 217 acceptable = request.headers.elements('Accept-Encoding') 218 if not acceptable: 219 if debug: 220 cherrypy.log('No Accept-Encoding', context='TOOLS.GZIP') 221 return 222 ct = response.headers.get('Content-Type', '').split(';')[0] 223 for coding in acceptable: 224 if coding.value == 'identity' and coding.qvalue != 0: 225 if debug: 226 cherrypy.log('Non-zero identity qvalue: %s' % coding, context='TOOLS.GZIP') 227 return 228 if coding.value in ('gzip', 'x-gzip'): 229 if coding.qvalue == 0: 230 if debug: 231 cherrypy.log('Zero gzip qvalue: %s' % coding, context='TOOLS.GZIP') 232 return 233 if ct not in mime_types: 234 found = False 235 if '/' in ct: 236 ct_media_type, ct_sub_type = ct.split('/') 237 for mime_type in mime_types: 238 if '/' in mime_type: 239 media_type, sub_type = mime_type.split('/') 240 if ct_media_type == media_type: 241 if sub_type == '*': 242 found = True 243 break 244 elif '+' in sub_type and '+' in ct_sub_type: 245 ct_left, ct_right = ct_sub_type.split('+') 246 left, right = sub_type.split('+') 247 if left == '*' and ct_right == right: 248 found = True 249 break 250 251 if not found: 252 if debug: 253 cherrypy.log('Content-Type %s not in mime_types %r' % (ct, mime_types), context='TOOLS.GZIP') 254 return 255 if debug: 256 cherrypy.log('Gzipping', context='TOOLS.GZIP') 257 response.headers['Content-Encoding'] = 'gzip' 258 response.body = compress(response.body, compress_level) 259 if 'Content-Length' in response.headers: 260 del response.headers['Content-Length'] 261 return 262 263 if debug: 264 cherrypy.log('No acceptable encoding found.', context='GZIP') 265 cherrypy.HTTPError(406, 'identity, gzip').set_response()