Robert Brewer avatar Robert Brewer committed 38dc014

Fix for #493 (connection not being closed properly).

Comments (0)

Files changed (2)

cherrypy/_cpwsgiserver.py

                 self.wsgi_app = wsgi_app
                 break
         else:
-            msg = "Not Found"
-            proto = self.environ.get("SERVER_PROTOCOL", "HTTP/1.0")
-            self.wfile.write("%s 404 %s\r\n" % (proto, msg))
-            self.wfile.write("Content-Length: %s\r\n\r\n" % len(msg))
-            self.wfile.write(msg)
-            self.wfile.flush()
-            self.ready = False
+            self.abort("404 Not Found")
             return
         
         self.environ["QUERY_STRING"] = qs
         # then all the http headers
         headers = mimetools.Message(self.rfile)
         self.environ["CONTENT_TYPE"] = headers.getheader("Content-type", "")
-        self.environ["CONTENT_LENGTH"] = headers.getheader("Content-length", "")
+        cl = headers.getheader("Content-length")
+        if method in ("POST", "PUT") and cl is None:
+            # No Content-Length header supplied. This will hang
+            # cgi.FieldStorage, since it cannot determine when to
+            # stop reading from the socket. Until we handle chunked
+            # encoding, always respond with 411 Length Required.
+            # See http://www.cherrypy.org/ticket/493.
+            self.abort("411 Length Required")
+            return
+        self.environ["CONTENT_LENGTH"] = cl or ""
+        
         for (k, v) in headers.items():
             envname = "HTTP_" + k.upper().replace("-","_")
             self.environ[envname] = v
         self.ready = True
     
+    def abort(self, status, msg=""):
+        """Write a simple error message back to the client."""
+        proto = self.environ.get("SERVER_PROTOCOL", "HTTP/1.0")
+        self.wfile.write("%s %s\r\n" % (proto, status))
+        self.wfile.write("Content-Length: %s\r\n\r\n" % len(msg))
+        if msg:
+            self.wfile.write(msg)
+        self.wfile.flush()
+        self.ready = False
+    
     def start_response(self, status, headers, exc_info = None):
         if self.started_response:
             if not exc_info:
     RequestHandlerClass = HTTPRequest
     
     def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
-                 max=-1, request_queue_size=5):
-        '''
-        be careful w/ max
-        '''
+                 max=-1, request_queue_size=5, timeout=10):
+        """Be careful w/ max"""
         self.requests = Queue.Queue(max)
         
         if callable(wsgi_app):
         self.server_name = server_name
         self.request_queue_size = request_queue_size
         self._workerThreads = []
+        
+        self.timeout = timeout
     
     def start(self):
         """Run the server forever."""
     def tick(self):
         try:
             s, addr = self.socket.accept()
-            if hasattr(s, 'setblocking'):
-                s.setblocking(1)
+            if hasattr(s, 'settimeout'):
+                s.settimeout(self.timeout)
             request = self.RequestHandlerClass(s, addr, self)
             self.requests.put(request)
         except socket.timeout:

cherrypy/test/test_http.py

+"""Tests for managing HTTP issues (malformed requests, etc).
+
+Some of these tests check timeouts, etcetera, and therefore take a long
+time to run. Therefore, this module should probably not be included in
+the 'comprehensive' test suite (test.py). Note, however, that we default
+to the builtin HTTP server (most tests default to 'serverless').
+"""
+
+import test
+test.prefer_parent_path()
+
+import httplib
+import cherrypy
+
+
+class Root:
+    def index(self, *args, **kwargs):
+        return "Hello world!"
+    index.exposed = True
+cherrypy.tree.mount(Root())
+
+cherrypy.config.update({
+    'global': {'server.log_to_screen': False,
+               'server.environment': 'production',
+               'server.show_tracebacks': True,
+               },
+})
+
+import helper
+
+class HTTPTests(helper.CPWebCase):
+    
+    def test_sockets(self):
+        if cherrypy.server.httpserver:
+            # By not including a Content-Length header, cgi.FieldStorage
+            # will hang. Verify that CP times out the socket and responds
+            # with 411 Length Required.
+            c = httplib.HTTPConnection("localhost:%s" % self.PORT)
+            c.request("POST", "/")
+            self.assertEqual(c.getresponse().status, 411)
+
+
+if __name__ == '__main__':
+    helper.testmain(server="cherrypy._cpwsgi.WSGIServer")
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.