Commits

Robert Brewer  committed dfe3d11

Fixes to WSGIServer. Fixes to test suite, so at least someone can start figuring out why testCore is still broken.

  • Participants
  • Parent commits 404f2a5
  • Branches cherrypy

Comments (0)

Files changed (3)

File _cpserver.py

     """Prepare the requested server and then run it."""
     
     # Instantiate the server.
-    if serverClass is None:
-        confclass = cpg.config.get("server.class")
-        if confclass:
-            serverClass = attributes(confclass)
-        else:
-            import _cpwsgi
-            serverClass = _cpwsgi.WSGIServer
+    if serverClass is None:
+        serverClass = cpg.config.get("server.class", None)
+    if serverClass and isinstance(serverClass, basestring):
+        serverClass = attributes(serverClass)
+    if serverClass is None:
+        import _cpwsgi
+        serverClass = _cpwsgi.WSGIServer
     
     cpg._httpserver = serverClass()
     

File _cpwsgiserver.py

 """
 
 import socket
-import thread
+import threading
 import Queue
 import mimetools # todo: use email
 import sys
         self.wfile.close()
         self.socket.close()
 
-def worker_thread(server):
-    while True:
-        try:
-            request = server.requests.get()
-            request.parse_request()
-            response = server.wsgi_app(request.environ, request.start_response)
-            for line in response: # write the response into the buffer
-                request.write(line)
-            request.terminate()
-        except:
-            server.handle_exception()
+
+class WorkerThread(threading.Thread):
+    
+    def __init__(self, server):
+        self.server = server
+        threading.Thread.__init__(self)
+    
+    def run(self):
+        while self.server._running:
+            try:
+                request = self.server.requests.get(block=False, timeout=1)
+                request.parse_request()
+                response = self.server.wsgi_app(request.environ, request.start_response)
+                for line in response: # write the response into the buffer
+                    request.write(line)
+                request.terminate()
+            except Queue.Empty:
+                pass
+            except (KeyboardInterrupt, SystemExit):
+                self.server.stop(callingThread=self)
+                raise
+            except:
+                self.server.handle_exception()
+
 
 class CherryPyWSGIServer(object):
-    version = "CherryPyWSGIServer/1.0" # none of this 0.1 uncertainty business
-    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, stderr=sys.stderr, bufsize=-1, max=-1):
+    version = "CherryPyWSGIServer/1.01" # none of this 0.1 uncertainty business
+    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
+                 stderr=sys.stderr, bufsize=-1, max=-1):
         '''
         be careful w/ max
         '''
         self.requests = Queue.Queue(max)
         self.wsgi_app = wsgi_app
         self.bind_addr = bind_addr
-        self.numthreads = numthreads
+        self.numthreads = numthreads or 1
         if server_name:
             self.server_name = server_name
         else:
             self.server_name = socket.gethostname()
         self.stderr = stderr
         self.bufsize = bufsize
-    def create_thread_pool(self):
-        for i in xrange(0, self.numthreads):
-            thread.start_new_thread(worker_thread, (self,))
+        self._workerThreads = []
+    
     def start(self):
         '''
         run the server forever
         self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
         self.socket.bind(self.bind_addr)
         self.socket.listen(5)
-        self.create_thread_pool()
-        while True:
-            self.tick()
+        
+        self._running = True
+        
+        # Create worker threads
+        for i in xrange(self.numthreads):
+            self._workerThreads.append(WorkerThread(self))
+        for worker in self._workerThreads:
+            worker.start()
+        
+        try:
+            while self._running:
+                self.tick()
+        except (KeyboardInterrupt, SystemExit):
+            self.stop()
+            raise
+    
+    def stop(self, callingThread=None):
+        """Gracefully shutdown a server that is serving forever."""
+        self._running = False
+        
+        # Must shut down threads here so the code that calls
+        # this method can know when all threads are stopped.
+        for worker in self._workerThreads:
+            if worker is not callingThread:
+                worker.join()
+        self._workerThreads = []
+    
     def tick(self):
         s, addr = self.socket.accept()
         request = HTTPRequest(s, addr, self)
         self.requests.put(request)
         # optimized version follows
         #self.requests.put(HTTPRequest(*self.socket.accept()))
+    
     def handle_exception(self):
         traceback.print_exc()
         

File test/helper.py

          repr(cpg.response.headerMap), repr(cpg.response.body)))
         return False
 
-def prepareCode(code):
+def prepareCode(code, serverClass):
     f = open('testsite.py', 'w')
     
     includePathsToSysPath = """
 def f(*a, **kw): return ""
 cpg.root._cpLogMessage = f
 '''
-    newcode = code.replace('cpg.config.update', beforeStart + 'cpg.config.update')
+    newcode = code.replace('cpg.config.update', beforeStart + 'cpg.config.update')
+    if serverClass:
+        serverClass = "serverClass='%s'" % serverClass
     newcode = newcode.replace('cpg.server.start(',
                               'cpg.config.configMap["/"]["server.logToScreen"] = False\n'
-                              'cpg.server.start(')
+                              'cpg.server.start(' + serverClass)
     f.write(newcode)
     
     f.close()
 
 def checkPageResult(testName, infoMap, code, testList, failedList, extraConfig = '', extraRequestHeader = []):
     response = None
-    prepareCode(code)
     
     # Try it in all 4 modes (regular, threadPooling x normal, WSGI)
-    native = 'server.class = "cherrypy._cphttpserver.embedded_server"'
-    modeList = [('r', native),
-                ('tp', native + '\nserver.threadPool = 3'),
-                ('r_wsgi', ""),
-                ('tp_wsgi', 'server.threadPool = 3')]
-    for mode, modeConfig in modeList:
-        f = open("testsite.cfg", "w")
-        f.write(extraConfig)
-        f.write('''
-[/]
-session.storageType = "ram"
-server.socketPort = 8000
-server.environment = "production"
-server.logToScreen = False
-''')
-        f.write(modeConfig)
-        f.close()
-
-        pid = startServer(infoMap)
-        passed=True
-        cookies=None
-        for url, rule in testList:
-            cpg, cookies = getPage(url, cookies, extraRequestHeader)
-            if not checkResult(testName, infoMap, mode, cpg, rule, failedList):
-                passed = 0
-                print "*** FAILED ***"
-                break
-        shutdownServer(pid, mode)
-        if passed:
-            print mode + "...",
-            sys.stdout.flush()
-        else:
-            break
+    for name, serverClass in [("native", "cherrypy._cphttpserver.embedded_server"),
+                              ("wsgi", "")]:
+        sys.stdout.write(name + ": ")
+        prepareCode(code, serverClass)
+        for mode, modeConfig in [('r', ""), ('tp', 'server.threadPool = 3')]:
+            sys.stdout.write(mode)
+            sys.stdout.flush()
+            f = open("testsite.cfg", "w")
+            f.write(extraConfig)
+            f.write('''
+[/]
+session.storageType = "ram"
+server.socketPort = 8000
+server.environment = "production"
+server.logToScreen = False
+''')
+            f.write(modeConfig + "\n")
+            f.close()
+
+            pid = startServer(infoMap)
+            passed=True
+            cookies=None
+            for url, rule in testList:
+                sys.stdout.write(".")
+                sys.stdout.flush()
+                cpg, cookies = getPage(url, cookies, extraRequestHeader)
+                if not checkResult(testName, infoMap, mode, cpg, rule, failedList):
+                    passed = 0
+                    print "*** FAILED ***"
+                    break
+            if passed:
+                sys.stdout.write("ok ")
+                sys.stdout.flush()
+            shutdownServer(pid, mode)
+            if not passed:
+                break
     if passed:
         print "passed"
     sys.stdout.flush()