NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

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()