Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/trunk/cherrypy/lib/safemime.py

Revision 1678 (checked in by fumanchu, 1 year ago)

Fix for #648 (Flash 8 upload socket timeout). Optional safe_multipart tool in lib.

  • Property svn:eol-style set to native
Line 
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             # Return '' if we've read all the data.
29             if self.bytes_read >= self.clen:
30                 return ''
31            
32             # Reduce 'size' if it's over our limit.
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                 # Return '' if we've read all the data.
45                 if self.bytes_read >= self.clen:
46                     return ''
47                
48                 # Reduce 'size' if it's over our limit.
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         # User didn't specify a size ...
58         # We read the line in chunks to make sure it's not a 100MB line !
59         res = []
60         size = 256
61         while True:
62             if self.clen:
63                 # Return if we've read all the data.
64                 if self.bytes_read >= self.clen:
65                     return ''.join(res)
66                
67                 # Reduce 'size' if it's over our limit.
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             # See http://www.cherrypy.org/ticket/421
76             if len(data) < size or data[-1:] == "\n":
77                 return ''.join(res)
78    
79     def readlines(self, sizehint = 0):
80         # Shamelessly stolen from StringIO
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             # Return '' if we've read all the data.
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
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets