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

Changeset 1565

Show
Ignore:
Timestamp:
12/24/06 22:09:17
Author:
fumanchu
Message:

Fix for #629 (fails to send Content-Length during HTTP 1.0 Keep-Alive). Also fixed a TE bug.

Files:

Legend:

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

    r1470 r1565  
    538538            if dict.get(headers, 'Content-Length') is None: 
    539539                dict.pop(headers, 'Content-Length', None) 
    540         elif code < 200 or code in (204, 304): 
     540        elif code < 200 or code in (204, 205, 304): 
    541541            # "All 1xx (informational), 204 (no content), 
    542542            # and 304 (not modified) responses MUST NOT 
  • trunk/cherrypy/test/test_conn.py

    r1550 r1565  
    100100     
    101101    def _streaming(self, set_cl): 
    102         if cherrypy.server.protocol_version != "HTTP/1.1": 
    103             print "skipped ", 
    104             return 
    105          
    106         self.PROTOCOL = "HTTP/1.1" 
    107          
    108         self.persistent = True 
    109          
    110         # Make the first request and assert there's no "Connection: close". 
    111         self.getPage("/") 
    112         self.assertStatus('200 OK') 
    113         self.assertBody(pov) 
    114         self.assertNoHeader("Connection") 
    115          
    116         # Make another, streamed request on the same connection. 
    117         if set_cl: 
    118             # When a Content-Length is provided, the content should stream 
    119             # without closing the connection. 
    120             self.getPage("/stream?set_cl=Yes") 
    121             self.assertHeader("Content-Length") 
    122             self.assertNoHeader("Connection", "close") 
    123             self.assertNoHeader("Transfer-Encoding") 
     102        if cherrypy.server.protocol_version == "HTTP/1.1": 
     103            self.PROTOCOL = "HTTP/1.1" 
     104             
     105            self.persistent = True 
     106             
     107            # Make the first request and assert there's no "Connection: close". 
     108            self.getPage("/") 
     109            self.assertStatus('200 OK') 
     110            self.assertBody(pov) 
     111            self.assertNoHeader("Connection") 
     112             
     113            # Make another, streamed request on the same connection. 
     114            if set_cl: 
     115                # When a Content-Length is provided, the content should stream 
     116                # without closing the connection. 
     117                self.getPage("/stream?set_cl=Yes") 
     118                self.assertHeader("Content-Length") 
     119                self.assertNoHeader("Connection", "close") 
     120                self.assertNoHeader("Transfer-Encoding") 
     121                 
     122                self.assertStatus('200 OK') 
     123                self.assertBody('0123456789') 
     124            else: 
     125                # When no Content-Length response header is provided, 
     126                # streamed output will either close the connection, or use 
     127                # chunked encoding, to determine transfer-length. 
     128                self.getPage("/stream") 
     129                self.assertNoHeader("Content-Length") 
     130                self.assertStatus('200 OK') 
     131                self.assertBody('0123456789') 
     132                 
     133                chunked_response = False 
     134                for k, v in self.headers: 
     135                    if k.lower() == "transfer-encoding": 
     136                        if str(v) == "chunked": 
     137                            chunked_response = True 
     138                 
     139                if chunked_response: 
     140                    self.assertNoHeader("Connection", "close") 
     141                else: 
     142                    self.assertHeader("Connection", "close") 
     143                     
     144                    # Make another request on the same connection, which should error. 
     145                    self.assertRaises(httplib.NotConnected, self.getPage, "/") 
    124146        else: 
    125             # When no Content-Length response header is provided, 
    126             # streamed output will either close the connection, or use 
    127             # chunked encoding, to determine transfer-length. 
    128             self.getPage("/stream") 
    129             self.assertNoHeader("Content-Length") 
    130              
    131             chunked_response = False 
    132             for k, v in self.headers: 
    133                 if k.lower() == "transfer-encoding": 
    134                     if str(v) == "chunked": 
    135                         chunked_response = True 
    136              
    137             if chunked_response: 
    138                 self.assertNoHeader("Connection", "close") 
     147            self.PROTOCOL = "HTTP/1.0" 
     148             
     149            self.persistent = True 
     150             
     151            # Make the first request and assert Keep-Alive. 
     152            self.getPage("/", headers=[("Connection", "Keep-Alive")]) 
     153            self.assertStatus('200 OK') 
     154            self.assertBody(pov) 
     155            self.assertHeader("Connection", "Keep-Alive") 
     156             
     157            # Make another, streamed request on the same connection. 
     158            if set_cl: 
     159                # When a Content-Length is provided, the content should 
     160                # stream without closing the connection. 
     161                self.getPage("/stream?set_cl=Yes", 
     162                             headers=[("Connection", "Keep-Alive")]) 
     163                self.assertHeader("Content-Length") 
     164                self.assertHeader("Connection", "Keep-Alive") 
     165                self.assertNoHeader("Transfer-Encoding") 
     166                self.assertStatus('200 OK') 
     167                self.assertBody('0123456789') 
    139168            else: 
    140                 self.assertHeader("Connection", "close") 
     169                # When a Content-Length is not provided, 
     170                # the server should close the connection. 
     171                self.getPage("/stream", headers=[("Connection", "Keep-Alive")]) 
     172                self.assertStatus('200 OK') 
     173                self.assertBody('0123456789') 
     174                 
     175                self.assertNoHeader("Content-Length") 
     176                self.assertNoHeader("Connection", "Keep-Alive") 
     177                self.assertNoHeader("Transfer-Encoding") 
    141178                 
    142179                # Make another request on the same connection, which should error. 
    143180                self.assertRaises(httplib.NotConnected, self.getPage, "/") 
    144          
    145         self.assertStatus('200 OK') 
    146         self.assertBody('0123456789') 
    147181     
    148182    def test_HTTP11_Timeout(self): 
  • trunk/cherrypy/wsgiserver.py

    r1564 r1565  
    237237            if environ.get("HTTP_CONNECTION", "") == "close": 
    238238                self.close_connection = True 
    239                 self.outheaders.append(("Connection", "close")) 
    240239        else: 
    241240            # HTTP/1.0 
    242             if environ.get("HTTP_CONNECTION", "") == "Keep-Alive": 
    243                 if self.close_connection == False: 
    244                     self.outheaders.append(("Connection", "Keep-Alive")) 
    245             else: 
     241            if environ.get("HTTP_CONNECTION", "") != "Keep-Alive": 
    246242                self.close_connection = True 
    247243         
    248244        # Transfer-Encoding support 
    249         te = environ.get("HTTP_TRANSFER_ENCODING", "") 
    250         te = [x.strip() for x in te.split(",") if x.strip()] 
     245        te = None 
     246        if self.response_protocol == "HTTP/1.1": 
     247            te = environ.get("HTTP_TRANSFER_ENCODING") 
     248            if te: 
     249                te = [x.strip().lower() for x in te.split(",") if x.strip()] 
     250         
     251        read_chunked = False 
     252         
    251253        if te: 
    252             while te: 
    253                 enc = te.pop() 
    254                 if enc.lower() == "chunked": 
    255                     if not self.decode_chunked(): 
    256                         return 
     254            for enc in te: 
     255                if enc == "chunked": 
     256                    read_chunked = True 
    257257                else: 
     258                    # Note that, even if we see "chunked", we must reject 
     259                    # if there is an extension we don't recognize. 
    258260                    self.simple_response("501 Unimplemented") 
    259261                    self.close_connection = True 
    260262                    return 
     263         
     264        if read_chunked: 
     265            if not self.decode_chunked(): 
     266                return 
    261267        else: 
    262268            cl = environ.get("CONTENT_LENGTH") 
     
    421427        status = int(self.status[:3]) 
    422428         
    423         if self.response_protocol == 'HTTP/1.1'
    424             if status == 413: 
    425                 # Request Entity Too Large. Close conn to avoid garbage. 
    426                 self.close_connection = True 
    427             elif "content-length" not in hkeys: 
    428                 # "All 1xx (informational), 204 (no content), 
    429                 # and 304 (not modified) responses MUST NOT 
    430                 # include a message-body." So no point chunking. 
    431                 if status < 200 or status in (204, 205, 304): 
    432                     pass 
    433                 else
     429        if status == 413
     430            # Request Entity Too Large. Close conn to avoid garbage. 
     431            self.close_connection = True 
     432        elif "content-length" not in hkeys: 
     433            # "All 1xx (informational), 204 (no content), 
     434            # and 304 (not modified) responses MUST NOT 
     435            # include a message-body." So no point chunking. 
     436            if status < 200 or status in (204, 205, 304): 
     437                pass 
     438            else: 
     439                if self.response_protocol == 'HTTP/1.1'
    434440                    # Use the chunked transfer-coding 
    435441                    self.chunked_write = True 
    436442                    self.outheaders.append(("Transfer-Encoding", "chunked")) 
    437          
    438         if self.close_connection and "connection" not in hkeys: 
    439             self.outheaders.append(("Connection", "close")) 
     443                else: 
     444                    # Closing the conn is the only way to determine len. 
     445                    self.close_connection = True 
     446         
     447        if "connection" not in hkeys: 
     448            if self.response_protocol == 'HTTP/1.1': 
     449                if self.close_connection: 
     450                    self.outheaders.append(("Connection", "close")) 
     451            else: 
     452                if not self.close_connection: 
     453                    self.outheaders.append(("Connection", "Keep-Alive")) 
    440454         
    441455        if "date" not in hkeys: 

Hosted by WebFaction

Log in as guest/cpguest to create tickets