Issue #880 new

HTTP persistent connection bug in CherryPy 3.0.3 on HTTP 503 error

guest
created an issue

Hello!

I'm not sure this bug exists anymore in the latest version of CherryPy since the code seems to have been shuffled around a bit (e.g., _cpengine.py, where this bug exists in 3.0.3 no longer exists or has been renamed), but I thought it would be a good idea to report it nevertheless.

In CherryPy 3.0.3, if an HTTP POST request is received before CherryPy has been fully initialized, an HTTP 503 error reply is returned. However, the code that sends this reply does not consume the HTTP POST body, and thus if the client is using a persistent HTTP connection, the message boundaries become corrupted for subsequent requests (the HTTP POST body that wasn't consumed gets interpreted as the beginning of the next HTTP request on the persistent connection).

The expected behavior would be to consume the HTTP POST body if there is one. A subsequent HTTP request on a persistent connection wouuld then get interpreted correctly as usual by CherryPy.

Old (the run method in NotReadyPresent in _cpengine.py):

{{{

def run(self, method, path, query_string, protocol, headers, rfile):
    self.method = "GET"
    cherrypy.HTTPError(503, self.msg).set_response()
    cherrypy.response.finalize()
    return cherrypy.response

}}}

New (the run method in NotReadyPresent in _cpengine.py): {{{

def run(self, method, path, query_string, protocol, headers, rfile):
    self.method = method
    #                                                                       
    # If the HTTP request body has any data, read all of that               
    # data.  This is needed in order to make persistent                     
    # connections work.  If the body is not consumed here and we            
    # have a persistent connection, the unread HTTP body will be            
    # misinterpreted as being the start of the next HTTP request.           
    #                                                                       
    contentLength = None
    for key, value in headers:
        key = key.lower() # HTTP header keys are case-insensitive           
        if key == "content-length":
            contentLength = int(value)
    if contentLength is not None:
        msg = "NotReadyRequest, reading %d byte HTTP %s body for %s" % \
            (contentLength, method, path)
        cherrypy.log(msg)
        body = rfile.read(contentLength)
        msg = "NotReadyRequest, read %d byte HTTP %s body for %s" % \
            (contentLength, method, path)
        cherrypy.log(msg)

    cherrypy.HTTPError(503, self.msg).set_response()
    cherrypy.response.finalize()
    return cherrypy.response

}}}

To reproduce this, (1) write an HTTP client that does three things (a) tries to establish an HTTP connection, (b) once established, sends HTTP POST requests (my test used 131 bytes in the body) over the same (persistent) connection, (c) returns to (a) if the connection is broken, (2) start/stop CherryPy until a 503 error is observed by the client. Once a 503 error is observed by the client, subsequent requests by the client over the persistent connection will see HTTP errors.

Cheers!

Comments (0)

  1. Log in to comment