| 1 |
import cherrypy |
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
class MultipartWrapper(object): |
|---|
| 5 |
"""Wraps a file-like object, returning '' when Content-Length is reached. |
|---|
| 6 |
|
|---|
| 7 |
The cgi module's logic for reading multipart MIME messages doesn't |
|---|
| 8 |
allow the parts to know when the Content-Length for the entire message |
|---|
| 9 |
has been reached, and doesn't allow for multipart-MIME messages that |
|---|
| 10 |
omit the trailing CRLF (Flash 8's FileReference.upload(url), for example, |
|---|
| 11 |
does this). The read_lines_to_outerboundary function gets stuck in a loop |
|---|
| 12 |
until the socket times out. |
|---|
| 13 |
|
|---|
| 14 |
This rfile wrapper simply monitors the incoming stream. When a read is |
|---|
| 15 |
attempted past the Content-Length, it returns an empty string rather |
|---|
| 16 |
than timing out (of course, if the last read *overlaps* the C-L, you'll |
|---|
| 17 |
get the last bit of data up to C-L, and then the next read will return |
|---|
| 18 |
an empty string). |
|---|
| 19 |
""" |
|---|
| 20 |
|
|---|
| 21 |
def __init__(self, rfile, clen): |
|---|
| 22 |
self.rfile = rfile |
|---|
| 23 |
self.clen = clen |
|---|
| 24 |
self.bytes_read = 0 |
|---|
| 25 |
|
|---|
| 26 |
def read(self, size = None): |
|---|
| 27 |
if self.clen: |
|---|
| 28 |
|
|---|
| 29 |
if self.bytes_read >= self.clen: |
|---|
| 30 |
return '' |
|---|
| 31 |
|
|---|
| 32 |
|
|---|
| 33 |
new_bytes_read = self.bytes_read + size |
|---|
| 34 |
if new_bytes_read > self.clen: |
|---|
| 35 |
size = self.clen - self.bytes_read |
|---|
| 36 |
|
|---|
| 37 |
data = self.rfile.read(size) |
|---|
| 38 |
self.bytes_read += len(data) |
|---|
| 39 |
return data |
|---|
| 40 |
|
|---|
| 41 |
def readline(self, size = None): |
|---|
| 42 |
if size is not None: |
|---|
| 43 |
if self.clen: |
|---|
| 44 |
|
|---|
| 45 |
if self.bytes_read >= self.clen: |
|---|
| 46 |
return '' |
|---|
| 47 |
|
|---|
| 48 |
|
|---|
| 49 |
new_bytes_read = self.bytes_read + size |
|---|
| 50 |
if new_bytes_read > self.clen: |
|---|
| 51 |
size = self.clen - self.bytes_read |
|---|
| 52 |
|
|---|
| 53 |
data = self.rfile.readline(size) |
|---|
| 54 |
self.bytes_read += len(data) |
|---|
| 55 |
return data |
|---|
| 56 |
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 |
res = [] |
|---|
| 60 |
size = 256 |
|---|
| 61 |
while True: |
|---|
| 62 |
if self.clen: |
|---|
| 63 |
|
|---|
| 64 |
if self.bytes_read >= self.clen: |
|---|
| 65 |
return ''.join(res) |
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 |
new_bytes_read = self.bytes_read + size |
|---|
| 69 |
if new_bytes_read > self.clen: |
|---|
| 70 |
size = self.clen - self.bytes_read |
|---|
| 71 |
|
|---|
| 72 |
data = self.rfile.readline(size) |
|---|
| 73 |
self.bytes_read += len(data) |
|---|
| 74 |
res.append(data) |
|---|
| 75 |
|
|---|
| 76 |
if len(data) < size or data[-1:] == "\n": |
|---|
| 77 |
return ''.join(res) |
|---|
| 78 |
|
|---|
| 79 |
def readlines(self, sizehint = 0): |
|---|
| 80 |
|
|---|
| 81 |
total = 0 |
|---|
| 82 |
lines = [] |
|---|
| 83 |
line = self.readline() |
|---|
| 84 |
while line: |
|---|
| 85 |
lines.append(line) |
|---|
| 86 |
total += len(line) |
|---|
| 87 |
if 0 < sizehint <= total: |
|---|
| 88 |
break |
|---|
| 89 |
line = self.readline() |
|---|
| 90 |
return lines |
|---|
| 91 |
|
|---|
| 92 |
def close(self): |
|---|
| 93 |
self.rfile.close() |
|---|
| 94 |
|
|---|
| 95 |
def __iter__(self): |
|---|
| 96 |
return self.rfile |
|---|
| 97 |
|
|---|
| 98 |
def next(self): |
|---|
| 99 |
if self.clen: |
|---|
| 100 |
|
|---|
| 101 |
if self.bytes_read >= self.clen: |
|---|
| 102 |
return '' |
|---|
| 103 |
|
|---|
| 104 |
data = self.rfile.next() |
|---|
| 105 |
self.bytes_read += len(data) |
|---|
| 106 |
return data |
|---|
| 107 |
|
|---|
| 108 |
|
|---|
| 109 |
def safe_multipart(flash_only=False): |
|---|
| 110 |
"""Wrap request.rfile in a reader that won't crash on no trailing CRLF.""" |
|---|
| 111 |
h = cherrypy.request.headers |
|---|
| 112 |
if not h.get('Content-Type').startswith('multipart/'): |
|---|
| 113 |
return |
|---|
| 114 |
if flash_only and not 'Shockwave Flash' in h.get('User-Agent', ''): |
|---|
| 115 |
return |
|---|
| 116 |
|
|---|
| 117 |
clen = h.get('Content-Length', '0') |
|---|
| 118 |
try: |
|---|
| 119 |
clen = int(clen) |
|---|
| 120 |
except ValueError: |
|---|
| 121 |
return |
|---|
| 122 |
cherrypy.request.rfile = MultipartWrapper(cherrypy.request.rfile, clen) |
|---|
| 123 |
|
|---|
| 124 |
def init(): |
|---|
| 125 |
"""Create a Tool for safe_multipart and add it to cherrypy.tools.""" |
|---|
| 126 |
cherrypy.tools.safe_multipart = cherrypy.Tool('before_request_body', |
|---|
| 127 |
safe_multipart) |
|---|
| 128 |
|
|---|