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

Ticket #648: flash8.patch

  • lib/httptools.py

    old new  
    541541##            raise StopIteration() 
    542542        return data 
    543543 
     544 
     545class MultipartWrapper(object): 
     546    r"""Wraps a file-like object, returning '' when Content-Length is reached. 
     547     
     548    The cgi module's logic for reading multipart MIME messages doesn't 
     549    allow the parts to know when the Content-Length for the entire message 
     550    has been reached, and doesn't allow for multipart-MIME messages that 
     551    omit the trailing CRLF (Flash 8's FileReference.upload(url), for example, 
     552    does this). The read_lines_to_outerboundary function gets stuck in a loop 
     553    until the socket times out. 
     554     
     555    This rfile wrapper simply monitors the incoming stream. When a read is 
     556    attempted past the Content-Length, it returns an empty string rather 
     557    than timing out (of course, if the last read *overlaps* the C-L, you'll 
     558    get the last bit of data up to C-L, and then the next read will return 
     559    an empty string). 
     560    """ 
     561     
     562    def __init__(self, rfile, clen): 
     563        self.rfile = rfile 
     564        self.clen = clen 
     565        self.bytes_read = 0 
     566     
     567    def read(self, size = None): 
     568        if self.clen: 
     569            # Return '' if we've read all the data. 
     570            if self.bytes_read >= self.clen: 
     571                return '' 
     572             
     573            # Reduce 'size' if it's over our limit. 
     574            new_bytes_read = self.bytes_read + size 
     575            if new_bytes_read > self.clen: 
     576                size = self.clen - self.bytes_read 
     577         
     578        data = self.rfile.read(size) 
     579        self.bytes_read += len(data) 
     580        return data 
     581     
     582    def readline(self, size = None): 
     583        if size is not None: 
     584            if self.clen: 
     585                # Return '' if we've read all the data. 
     586                if self.bytes_read >= self.clen: 
     587                    return '' 
     588                 
     589                # Reduce 'size' if it's over our limit. 
     590                new_bytes_read = self.bytes_read + size 
     591                if new_bytes_read > self.clen: 
     592                    size = self.clen - self.bytes_read 
     593             
     594            data = self.rfile.readline(size) 
     595            self.bytes_read += len(data) 
     596            return data 
     597         
     598        # User didn't specify a size ... 
     599        # We read the line in chunks to make sure it's not a 100MB line ! 
     600        res = [] 
     601        size = 256 
     602        while True: 
     603            if self.clen: 
     604                # Return if we've read all the data. 
     605                if self.bytes_read >= self.clen: 
     606                    return ''.join(res) 
     607                 
     608                # Reduce 'size' if it's over our limit. 
     609                new_bytes_read = self.bytes_read + size 
     610                if new_bytes_read > self.clen: 
     611                    size = self.clen - self.bytes_read 
     612             
     613            data = self.rfile.readline(size) 
     614            self.bytes_read += len(data) 
     615            res.append(data) 
     616            # See http://www.cherrypy.org/ticket/421 
     617            if len(data) < size or data[-1:] == "\n": 
     618                return ''.join(res) 
     619     
     620    def readlines(self, sizehint = 0): 
     621        # Shamelessly stolen from StringIO 
     622        total = 0 
     623        lines = [] 
     624        line = self.readline() 
     625        while line: 
     626            lines.append(line) 
     627            total += len(line) 
     628            if 0 < sizehint <= total: 
     629                break 
     630            line = self.readline() 
     631        return lines 
     632     
     633    def close(self): 
     634        self.rfile.close() 
     635     
     636    def __iter__(self): 
     637        return self.rfile 
     638     
     639    def next(self): 
     640        if self.clen: 
     641            # Return '' if we've read all the data. 
     642            if self.bytes_read >= self.clen: 
     643                return '' 
     644         
     645        data = self.rfile.next() 
     646        self.bytes_read += len(data) 
     647        return data 
     648 
  • test/test_core.py

    old new  
    1717 
    1818 
    1919def setup_server(): 
     20     
     21    class FlashWrapper: 
     22         
     23        def before_request_body(self): 
     24            if not cherrypy.config.get("flashwrapper.on", False): 
     25                return 
     26             
     27            h = cherrypy.request.headers 
     28             
     29            if h.get('Content-Type').startswith('multipart/'): 
     30                if 'Shockwave Flash' in h.get('User-Agent', ''): 
     31                    clen = h.get('Content-Length', '0') 
     32                    try: 
     33                        clen = int(clen) 
     34                    except ValueError: 
     35                        return 
     36                    wrap = httptools.MultipartWrapper 
     37                    cherrypy.request.rfile = wrap(cherrypy.request.rfile, clen) 
     38     
    2039    class Root: 
    2140         
     41        _cp_filters = [FlashWrapper()] 
     42         
    2243        def index(self): 
    2344            return "hello" 
    2445        index.exposed = True 
     
    4364        def upload(self, file): 
    4465            return "Size: %s" % len(file.file.read()) 
    4566        upload.exposed = True 
     67         
     68        def flashupload(self, Filedata, Upload, Filename): 
     69            return ("Upload: %r, Filename: %r, Filedata: %r" % 
     70                    (Upload, Filename, Filedata.file.read())) 
     71        flashupload.exposed = True 
    4672     
    4773    cherrypy.root = Root() 
    4874 
     
    375401        '/error/rethrow': { 
    376402            'server.throw_errors': True, 
    377403        }, 
     404        '/flashupload': {'server.max_request_body_size': 0, 
     405                         'flashwrapper.on': True, 
     406                         }, 
    378407    }) 
    379408 
    380409 
     
    834863        self.assertHeader('Set-Cookie', 'First=Dinsdale;') 
    835864        self.assertHeader('Set-Cookie', 'Last=Piranha;') 
    836865     
     866    def test_Flash_Upload(self): 
     867        headers = [ 
     868            ('Accept', 'text/*'), 
     869            ('Content-Type', 'multipart/form-data; ' 
     870                 'boundary=----------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6'), 
     871            ('User-Agent', 'Shockwave Flash'), 
     872            ('Host', 'www01.cherrypy.org:8080'), 
     873            ('Content-Length', '499'), 
     874            ('Connection', 'Keep-Alive'), 
     875            ('Cache-Control', 'no-cache'), 
     876            ] 
     877        filedata = ('<?xml version="1.0" encoding="UTF-8"?>\r\n' 
     878                    '<projectDescription>\r\n' 
     879                    '</projectDescription>\r\n') 
     880        body = ( 
     881            '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n' 
     882            'Content-Disposition: form-data; name="Filename"\r\n' 
     883            '\r\n' 
     884            '.project\r\n' 
     885            '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n' 
     886            'Content-Disposition: form-data; ' 
     887                'name="Filedata"; filename=".project"\r\n' 
     888            'Content-Type: application/octet-stream\r\n' 
     889            '\r\n' 
     890            + filedata +  
     891            '\r\n' 
     892            '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n' 
     893            'Content-Disposition: form-data; name="Upload"\r\n' 
     894            '\r\n' 
     895            'Submit Query\r\n' 
     896            # Flash apps omit the trailing \r\n on the last line: 
     897            '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6--' 
     898            ) 
     899        self.getPage('/flashupload', headers, "POST", body) 
     900        self.assertBody("Upload: 'Submit Query', Filename: '.project', " 
     901                        "Filedata: %r" % filedata) 
     902         
    837903    def testMaxRequestSize(self): 
    838904        self.getPage("/", headers=[('From', "x" * 500)]) 
    839905        self.assertStatus(413) 

Hosted by WebFaction

Log in as guest/cpguest to create tickets