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

Changeset 1258

Show
Ignore:
Timestamp:
08/21/06 00:27:00
Author:
fumanchu
Message:

Fix for #497 (decode a "Transfer-Encoding: chunked" request).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/cherrypy/_cpwsgiserver.py

    r1257 r1258  
    77import rfc822 
    88import socket 
     9try: 
     10    import cStringIO as StringIO 
     11except ImportError: 
     12    import StringIO 
    913import sys 
    1014import threading 
     
    128132        # then all the http headers 
    129133        headers = mimetools.Message(self.rfile) 
    130         self.environ["CONTENT_TYPE"] = headers.getheader("Content-type", ""
     134        self.environ.update(self.parse_headers(headers)
    131135         
    132136        cl = headers.getheader("Content-length") 
     
    139143            self.simple_response("411 Length Required") 
    140144            return 
    141         self.environ["CONTENT_LENGTH"] = cl or "" 
    142          
    143         for k in headers: 
    144             envname = "HTTP_" + k.upper().replace("-", "_") 
    145             if k in comma_separated_headers: 
    146                 self.environ[envname] = ", ".join(headers.getheaders(k)) 
    147             else: 
    148                 self.environ[envname] = headers[k] 
    149          
     145         
     146        # Persistent connection support 
    150147        if self.environ["SERVER_PROTOCOL"] == "HTTP/1.1": 
    151148            if headers.getheader("Connection", "") == "close": 
     
    158155            else: 
    159156                self.close_connection = True 
     157         
     158        # Transfer-Encoding support 
     159        te = headers.getheader("Transfer-Encoding", "") 
     160        te = [x.strip() for x in te.split(",") if x.strip()] 
     161        while te: 
     162            enc = te.pop() 
     163            if enc.lower() == "chunked": 
     164                if not self.decode_chunked(): 
     165                    return 
     166            else: 
     167                self.simple_response("501 Unimplemented") 
     168                self.close_connection = True 
     169                return 
    160170         
    161171        # From PEP 333: 
     
    180190         
    181191        self.ready = True 
     192     
     193    def parse_headers(self, headers): 
     194        environ = {} 
     195        environ["CONTENT_TYPE"] = headers.getheader("Content-type", "") 
     196        environ["CONTENT_LENGTH"] = headers.getheader("Content-length") or "" 
     197         
     198        for k in headers: 
     199            envname = "HTTP_" + k.upper().replace("-", "_") 
     200            if k in comma_separated_headers: 
     201                environ[envname] = ", ".join(headers.getheaders(k)) 
     202            elif k in ('Transfer-Encoding',): 
     203                pass 
     204            else: 
     205                environ[envname] = headers[k] 
     206        return environ 
     207     
     208    def decode_chunked(self): 
     209        """Decode the 'chunked' transfer coding.""" 
     210        cl = 0 
     211        data = StringIO.StringIO() 
     212        while True: 
     213            line = self.rfile.readline().strip().split(" ", 1) 
     214            chunk_size = int(line.pop(0), 16) 
     215            if chunk_size <= 0: 
     216                break 
     217##            if line: chunk_extension = line[0] 
     218            cl += chunk_size 
     219            data.write(self.rfile.read(chunk_size)) 
     220            if self.rfile.read(2) != "\r\n": 
     221                self.simple_response("400 Bad Request", 
     222                                     "Bad chunked transfer coding") 
     223                return 
     224         
     225        headers = mimetools.Message(self.rfile) 
     226        self.environ.update(self.parse_headers(headers)) 
     227        data.seek(0) 
     228        self.environ["wsgi.input"] = data 
     229        self.environ["CONTENT_LENGTH"] = str(cl) or "" 
     230        return True 
    182231     
    183232    def respond(self): 
  • trunk/cherrypy/test/test_conn.py

    r1257 r1258  
    3232         
    3333        def upload(self): 
    34             return "thanks for the %s bytes!" % len(cherrypy.request.body.read()) 
     34            return ("thanks for '%s' (%s)" % 
     35                    (cherrypy.request.body.read(), 
     36                     cherrypy.request.headers['Content-Type'])) 
    3537        upload.exposed = True 
    3638     
     
    3840    cherrypy.config.update({ 
    3941        'log_to_screen': False, 
     42        'show_tracebacks': True, 
    4043        'environment': 'production', 
    4144        }) 
     
    8588     
    8689    def test_HTTP11_pipelining(self): 
     90        self.PROTOCOL = "HTTP/1.1" 
     91         
    8792        # Test pipelining. httplib doesn't support this directly. 
    8893        conn = httplib.HTTPConnection(self.HOST, self.PORT) 
     
    117122     
    118123    def test_100_Continue(self): 
     124        self.PROTOCOL = "HTTP/1.1" 
     125         
    119126        conn = httplib.HTTPConnection(self.HOST, self.PORT) 
    120127        conn.auto_open = False 
     
    160167        self.status, self.headers, self.body = webtest.shb(response) 
    161168        self.assertStatus(200) 
    162         self.assertBody("thanks for the 17 bytes!") 
     169        self.assertBody("thanks for 'I am a small file' (text/plain)") 
     170     
     171    def test_Chunked_Encoding(self): 
     172        self.PROTOCOL = "HTTP/1.1" 
     173         
     174        # Set our HTTP_CONN to an instance so it persists between requests. 
     175        self.HTTP_CONN = httplib.HTTPConnection(self.HOST, self.PORT) 
     176         
     177        # Try a normal chunked request 
     178        body = ("8\r\nxx\r\nxxxx\r\n5\r\nyyyyy\r\n0\r\n" 
     179                "Content-Type: application/x-json\r\n\r\n") 
     180        self.getPage("/upload", 
     181                     headers=[("Transfer-Encoding", "chunked"), 
     182                              ("Trailer", "Content-Type"), 
     183                              ("Content-Length", len(body)), 
     184                              ], 
     185                     body=body, method="POST") 
     186        self.assertStatus('200 OK') 
     187        self.assertBody("thanks for 'xx\r\nxxxxyyyyy' (application/x-json)") 
    163188     
    164189    def test_HTTP10(self): 

Hosted by WebFaction

Log in as guest/cpguest to create tickets