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

_cpreqbody.py (17816B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\_cpreqbody.py
      2 import re
      3 import sys
      4 import tempfile
      5 from urllib import unquote_plus
      6 import cherrypy
      7 from cherrypy._cpcompat import basestring, ntob, ntou
      8 from cherrypy.lib import httputil
      9 
     10 def process_urlencoded(entity):
     11     qs = entity.fp.read()
     12     for charset in entity.attempt_charsets:
     13         try:
     14             params = {}
     15             for aparam in qs.split(ntob('&')):
     16                 for pair in aparam.split(ntob(';')):
     17                     if not pair:
     18                         continue
     19                     atoms = pair.split(ntob('='), 1)
     20                     if len(atoms) == 1:
     21                         atoms.append(ntob(''))
     22                     key = unquote_plus(atoms[0]).decode(charset)
     23                     try:
     24                         value = unquote_plus(atoms[1]).decode(charset)
     25                     except Exception:
     26                         value = unquote_plus(atoms[1])
     27 
     28                     if key in params:
     29                         if not isinstance(params[key], list):
     30                             params[key] = [params[key]]
     31                         params[key].append(value)
     32                     else:
     33                         params[key] = value
     34 
     35         except UnicodeDecodeError:
     36             pass
     37         else:
     38             entity.charset = charset
     39             break
     40 
     41     else:
     42         raise cherrypy.HTTPError(400, 'The request entity could not be decoded. The following charsets were attempted: %s' % repr(entity.attempt_charsets))
     43 
     44     for key, value in params.items():
     45         if key in entity.params:
     46             if not isinstance(entity.params[key], list):
     47                 entity.params[key] = [entity.params[key]]
     48             entity.params[key].append(value)
     49         else:
     50             entity.params[key] = value
     51 
     52 
     53 def process_multipart(entity):
     54     ib = ''
     55     if 'boundary' in entity.content_type.params:
     56         ib = entity.content_type.params['boundary'].strip('"')
     57     if not re.match('^[ -~]{0,200}[!-~]$', ib):
     58         raise ValueError('Invalid boundary in multipart form: %r' % (ib,))
     59     ib = ('--' + ib).encode('ascii')
     60     while True:
     61         b = entity.readline()
     62         if not b:
     63             return
     64         b = b.strip()
     65         if b == ib:
     66             break
     67 
     68     while True:
     69         part = entity.part_class.from_fp(entity.fp, ib)
     70         entity.parts.append(part)
     71         part.process()
     72         if part.fp.done:
     73             break
     74 
     75 
     76 def process_multipart_form_data(entity):
     77     process_multipart(entity)
     78     kept_parts = []
     79     for part in entity.parts:
     80         if part.name is None:
     81             kept_parts.append(part)
     82         else:
     83             if part.filename is None:
     84                 value = part.fullvalue()
     85             else:
     86                 value = part
     87             if part.name in entity.params:
     88                 if not isinstance(entity.params[part.name], list):
     89                     entity.params[part.name] = [entity.params[part.name]]
     90                 entity.params[part.name].append(value)
     91             else:
     92                 entity.params[part.name] = value
     93 
     94     entity.parts = kept_parts
     95 
     96 
     97 def _old_process_multipart(entity):
     98     process_multipart(entity)
     99     params = entity.params
    100     for part in entity.parts:
    101         if part.name is None:
    102             key = ntou('parts')
    103         else:
    104             key = part.name
    105         if part.filename is None:
    106             value = part.fullvalue()
    107         else:
    108             value = part
    109         if key in params:
    110             if not isinstance(params[key], list):
    111                 params[key] = [params[key]]
    112             params[key].append(value)
    113         else:
    114             params[key] = value
    115 
    116 
    117 class Entity(object):
    118     attempt_charsets = ['utf-8']
    119     charset = None
    120     content_type = None
    121     default_content_type = 'application/x-www-form-urlencoded'
    122     filename = None
    123     fp = None
    124     headers = None
    125     length = None
    126     name = None
    127     params = None
    128     processors = {'application/x-www-form-urlencoded': process_urlencoded,
    129      'multipart/form-data': process_multipart_form_data,
    130      'multipart': process_multipart}
    131     parts = None
    132     part_class = None
    133 
    134     def __init__(self, fp, headers, params = None, parts = None):
    135         self.processors = self.processors.copy()
    136         self.fp = fp
    137         self.headers = headers
    138         if params is None:
    139             params = {}
    140         self.params = params
    141         if parts is None:
    142             parts = []
    143         self.parts = parts
    144         self.content_type = headers.elements('Content-Type')
    145         if self.content_type:
    146             self.content_type = self.content_type[0]
    147         else:
    148             self.content_type = httputil.HeaderElement.from_str(self.default_content_type)
    149         dec = self.content_type.params.get('charset', None)
    150         if dec:
    151             self.attempt_charsets = [dec] + [ c for c in self.attempt_charsets if c != dec ]
    152         else:
    153             self.attempt_charsets = self.attempt_charsets[:]
    154         self.length = None
    155         clen = headers.get('Content-Length', None)
    156         if clen is not None and 'chunked' not in headers.get('Transfer-Encoding', ''):
    157             try:
    158                 self.length = int(clen)
    159             except ValueError:
    160                 pass
    161 
    162         self.name = None
    163         self.filename = None
    164         disp = headers.elements('Content-Disposition')
    165         if disp:
    166             disp = disp[0]
    167             if 'name' in disp.params:
    168                 self.name = disp.params['name']
    169                 if self.name.startswith('"') and self.name.endswith('"'):
    170                     self.name = self.name[1:-1]
    171             if 'filename' in disp.params:
    172                 self.filename = disp.params['filename']
    173                 if self.filename.startswith('"') and self.filename.endswith('"'):
    174                     self.filename = self.filename[1:-1]
    175 
    176     type = property(lambda self: self.content_type, doc='A deprecated alias for :attr:`content_type<cherrypy._cpreqbody.Entity.content_type>`.')
    177 
    178     def read(self, size = None, fp_out = None):
    179         return self.fp.read(size, fp_out)
    180 
    181     def readline(self, size = None):
    182         return self.fp.readline(size)
    183 
    184     def readlines(self, sizehint = None):
    185         return self.fp.readlines(sizehint)
    186 
    187     def __iter__(self):
    188         return self
    189 
    190     def next(self):
    191         line = self.readline()
    192         if not line:
    193             raise StopIteration
    194         return line
    195 
    196     def read_into_file(self, fp_out = None):
    197         if fp_out is None:
    198             fp_out = self.make_file()
    199         self.read(fp_out=fp_out)
    200         return fp_out
    201 
    202     def make_file(self):
    203         return tempfile.TemporaryFile()
    204 
    205     def fullvalue(self):
    206         if self.file:
    207             self.file.seek(0)
    208             value = self.file.read()
    209             self.file.seek(0)
    210         else:
    211             value = self.value
    212         return value
    213 
    214     def process(self):
    215         proc = None
    216         ct = self.content_type.value
    217         try:
    218             proc = self.processors[ct]
    219         except KeyError:
    220             toptype = ct.split('/', 1)[0]
    221             try:
    222                 proc = self.processors[toptype]
    223             except KeyError:
    224                 pass
    225 
    226         if proc is None:
    227             self.default_proc()
    228         else:
    229             proc(self)
    230 
    231     def default_proc(self):
    232         pass
    233 
    234 
    235 class Part(Entity):
    236     attempt_charsets = ['us-ascii', 'utf-8']
    237     boundary = None
    238     default_content_type = 'text/plain'
    239     maxrambytes = 1000
    240 
    241     def __init__(self, fp, headers, boundary):
    242         Entity.__init__(self, fp, headers)
    243         self.boundary = boundary
    244         self.file = None
    245         self.value = None
    246 
    247     def from_fp(cls, fp, boundary):
    248         headers = cls.read_headers(fp)
    249         return cls(fp, headers, boundary)
    250 
    251     from_fp = classmethod(from_fp)
    252 
    253     def read_headers(cls, fp):
    254         headers = httputil.HeaderMap()
    255         while True:
    256             line = fp.readline()
    257             if not line:
    258                 raise EOFError('Illegal end of headers.')
    259             if line == ntob('\r\n'):
    260                 break
    261             if not line.endswith(ntob('\r\n')):
    262                 raise ValueError('MIME requires CRLF terminators: %r' % line)
    263             if line[0] in ntob(' \t'):
    264                 v = line.strip().decode('ISO-8859-1')
    265             else:
    266                 k, v = line.split(ntob(':'), 1)
    267                 k = k.strip().decode('ISO-8859-1')
    268                 v = v.strip().decode('ISO-8859-1')
    269             existing = headers.get(k)
    270             if existing:
    271                 v = ', '.join((existing, v))
    272             headers[k] = v
    273 
    274         return headers
    275 
    276     read_headers = classmethod(read_headers)
    277 
    278     def read_lines_to_boundary(self, fp_out = None):
    279         endmarker = self.boundary + ntob('--')
    280         delim = ntob('')
    281         prev_lf = True
    282         lines = []
    283         seen = 0
    284         while True:
    285             line = self.fp.readline(65536)
    286             if not line:
    287                 raise EOFError('Illegal end of multipart body.')
    288             if line.startswith(ntob('--')) and prev_lf:
    289                 strippedline = line.strip()
    290                 if strippedline == self.boundary:
    291                     break
    292                 if strippedline == endmarker:
    293                     self.fp.finish()
    294                     break
    295             line = delim + line
    296             if line.endswith(ntob('\r\n')):
    297                 delim = ntob('\r\n')
    298                 line = line[:-2]
    299                 prev_lf = True
    300             elif line.endswith(ntob('\n')):
    301                 delim = ntob('\n')
    302                 line = line[:-1]
    303                 prev_lf = True
    304             else:
    305                 delim = ntob('')
    306                 prev_lf = False
    307             if fp_out is None:
    308                 lines.append(line)
    309                 seen += len(line)
    310                 if seen > self.maxrambytes:
    311                     fp_out = self.make_file()
    312                     for line in lines:
    313                         fp_out.write(line)
    314 
    315             else:
    316                 fp_out.write(line)
    317 
    318         if fp_out is None:
    319             result = ntob('').join(lines)
    320             for charset in self.attempt_charsets:
    321                 try:
    322                     result = result.decode(charset)
    323                 except UnicodeDecodeError:
    324                     pass
    325                 else:
    326                     self.charset = charset
    327                     return result
    328 
    329             else:
    330                 raise cherrypy.HTTPError(400, 'The request entity could not be decoded. The following charsets were attempted: %s' % repr(self.attempt_charsets))
    331 
    332         else:
    333             fp_out.seek(0)
    334             return fp_out
    335 
    336     def default_proc(self):
    337         if self.filename:
    338             self.file = self.read_into_file()
    339         else:
    340             result = self.read_lines_to_boundary()
    341             if isinstance(result, basestring):
    342                 self.value = result
    343             else:
    344                 self.file = result
    345 
    346     def read_into_file(self, fp_out = None):
    347         if fp_out is None:
    348             fp_out = self.make_file()
    349         self.read_lines_to_boundary(fp_out=fp_out)
    350         return fp_out
    351 
    352 
    353 Entity.part_class = Part
    354 
    355 class Infinity(object):
    356 
    357     def __cmp__(self, other):
    358         return 1
    359 
    360     def __sub__(self, other):
    361         return self
    362 
    363 
    364 inf = Infinity()
    365 comma_separated_headers = ['Accept',
    366  'Accept-Charset',
    367  'Accept-Encoding',
    368  'Accept-Language',
    369  'Accept-Ranges',
    370  'Allow',
    371  'Cache-Control',
    372  'Connection',
    373  'Content-Encoding',
    374  'Content-Language',
    375  'Expect',
    376  'If-Match',
    377  'If-None-Match',
    378  'Pragma',
    379  'Proxy-Authenticate',
    380  'Te',
    381  'Trailer',
    382  'Transfer-Encoding',
    383  'Upgrade',
    384  'Vary',
    385  'Via',
    386  'Warning',
    387  'Www-Authenticate']
    388 
    389 class SizedReader:
    390 
    391     def __init__(self, fp, length, maxbytes, bufsize = 8192, has_trailers = False):
    392         self.fp = fp
    393         self.length = length
    394         self.maxbytes = maxbytes
    395         self.buffer = ntob('')
    396         self.bufsize = bufsize
    397         self.bytes_read = 0
    398         self.done = False
    399         self.has_trailers = has_trailers
    400 
    401     def read(self, size = None, fp_out = None):
    402         if self.length is None:
    403             if size is None:
    404                 remaining = inf
    405             else:
    406                 remaining = size
    407         else:
    408             remaining = self.length - self.bytes_read
    409             if size and size < remaining:
    410                 remaining = size
    411         if remaining == 0:
    412             self.finish()
    413             if fp_out is None:
    414                 return ntob('')
    415             else:
    416                 return
    417         chunks = []
    418         if self.buffer:
    419             if remaining is inf:
    420                 data = self.buffer
    421                 self.buffer = ntob('')
    422             else:
    423                 data = self.buffer[:remaining]
    424                 self.buffer = self.buffer[remaining:]
    425             datalen = len(data)
    426             remaining -= datalen
    427             self.bytes_read += datalen
    428             if self.maxbytes and self.bytes_read > self.maxbytes:
    429                 raise cherrypy.HTTPError(413)
    430             if fp_out is None:
    431                 chunks.append(data)
    432             else:
    433                 fp_out.write(data)
    434         while remaining > 0:
    435             chunksize = min(remaining, self.bufsize)
    436             try:
    437                 data = self.fp.read(chunksize)
    438             except Exception:
    439                 e = sys.exc_info()[1]
    440                 if e.__class__.__name__ == 'MaxSizeExceeded':
    441                     raise cherrypy.HTTPError(413, 'Maximum request length: %r' % e.args[1])
    442                 else:
    443                     raise
    444 
    445             if not data:
    446                 self.finish()
    447                 break
    448             datalen = len(data)
    449             remaining -= datalen
    450             self.bytes_read += datalen
    451             if self.maxbytes and self.bytes_read > self.maxbytes:
    452                 raise cherrypy.HTTPError(413)
    453             if fp_out is None:
    454                 chunks.append(data)
    455             else:
    456                 fp_out.write(data)
    457 
    458         if fp_out is None:
    459             return ntob('').join(chunks)
    460 
    461     def readline(self, size = None):
    462         chunks = []
    463         while size is None or size > 0:
    464             chunksize = self.bufsize
    465             if size is not None and size < self.bufsize:
    466                 chunksize = size
    467             data = self.read(chunksize)
    468             if not data:
    469                 break
    470             pos = data.find(ntob('\n')) + 1
    471             if pos:
    472                 chunks.append(data[:pos])
    473                 remainder = data[pos:]
    474                 self.buffer += remainder
    475                 self.bytes_read -= len(remainder)
    476                 break
    477             else:
    478                 chunks.append(data)
    479 
    480         return ntob('').join(chunks)
    481 
    482     def readlines(self, sizehint = None):
    483         if self.length is not None:
    484             if sizehint is None:
    485                 sizehint = self.length - self.bytes_read
    486             else:
    487                 sizehint = min(sizehint, self.length - self.bytes_read)
    488         lines = []
    489         seen = 0
    490         while True:
    491             line = self.readline()
    492             if not line:
    493                 break
    494             lines.append(line)
    495             seen += len(line)
    496             if seen >= sizehint:
    497                 break
    498 
    499         return lines
    500 
    501     def finish(self):
    502         self.done = True
    503         if self.has_trailers and hasattr(self.fp, 'read_trailer_lines'):
    504             self.trailers = {}
    505             try:
    506                 for line in self.fp.read_trailer_lines():
    507                     if line[0] in ntob(' \t'):
    508                         v = line.strip()
    509                     else:
    510                         try:
    511                             k, v = line.split(ntob(':'), 1)
    512                         except ValueError:
    513                             raise ValueError('Illegal header line.')
    514 
    515                         k = k.strip().title()
    516                         v = v.strip()
    517                     if k in comma_separated_headers:
    518                         existing = self.trailers.get(envname)
    519                         if existing:
    520                             v = ntob(', ').join((existing, v))
    521                     self.trailers[k] = v
    522 
    523             except Exception:
    524                 e = sys.exc_info()[1]
    525                 if e.__class__.__name__ == 'MaxSizeExceeded':
    526                     raise cherrypy.HTTPError(413, 'Maximum request length: %r' % e.args[1])
    527                 else:
    528                     raise
    529 
    530 
    531 class RequestBody(Entity):
    532     bufsize = 8192
    533     default_content_type = ''
    534     maxbytes = None
    535 
    536     def __init__(self, fp, headers, params = None, request_params = None):
    537         Entity.__init__(self, fp, headers, params)
    538         if self.content_type.value.startswith('text/'):
    539             for c in ('ISO-8859-1', 'iso-8859-1', 'Latin-1', 'latin-1'):
    540                 if c in self.attempt_charsets:
    541                     break
    542             else:
    543                 self.attempt_charsets.append('ISO-8859-1')
    544 
    545         self.processors['multipart'] = _old_process_multipart
    546         if request_params is None:
    547             request_params = {}
    548         self.request_params = request_params
    549 
    550     def process(self):
    551         h = cherrypy.serving.request.headers
    552         if 'Content-Length' not in h and 'Transfer-Encoding' not in h:
    553             raise cherrypy.HTTPError(411)
    554         self.fp = SizedReader(self.fp, self.length, self.maxbytes, bufsize=self.bufsize, has_trailers='Trailer' in h)
    555         super(RequestBody, self).process()
    556         request_params = self.request_params
    557         for key, value in self.params.items():
    558             if isinstance(key, unicode):
    559                 key = key.encode('ISO-8859-1')
    560             if key in request_params:
    561                 if not isinstance(request_params[key], list):
    562                     request_params[key] = [request_params[key]]
    563                 request_params[key].append(value)
    564             else:
    565                 request_params[key] = value