Changeset 698
- Timestamp:
- 10/01/05 13:44:53
- Files:
-
- trunk/cherrypy/__init__.py (modified) (1 diff)
- trunk/cherrypy/_cphttpserver.py (modified) (2 diffs)
- trunk/cherrypy/_cpwsgiserver.py (modified) (3 diffs)
- trunk/cherrypy/server.py (modified) (10 diffs)
- trunk/cherrypy/test/helper.py (modified) (6 diffs)
- trunk/cherrypy/test/test_noserver.py (modified) (1 diff)
- trunk/cherrypy/test/test_states.py (modified) (8 diffs)
- trunk/cherrypy/test/test_tutorials.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/__init__.py
r665 r698 47 47 _appserver_state = 0 48 48 _httpserver = None 49 _httpserverclass = None 50 _interrupt = None 49 51 50 52 codecoverage = False trunk/cherrypy/_cphttpserver.py
r693 r698 223 223 self.handle_request() 224 224 if self.interrupt: 225 i = self.interrupt 226 self.interrupt = None 227 raise i 225 raise self.interrupt 228 226 start = serve_forever 229 227 … … 364 362 while self.ready: 365 363 if self.interrupt: 366 i = self.interrupt 367 self.interrupt = None 368 raise i 364 raise self.interrupt 369 365 if not self.handle_request(): 370 366 break trunk/cherrypy/_cpwsgiserver.py
r693 r698 117 117 self.wfile = self.socket.makefile("w", self.server.bufsize) 118 118 self.sent_headers = False 119 119 120 def parse_request(self): 120 121 self.sent_headers = False … … 323 324 self.tick() 324 325 if self.interrupt: 325 i = self.interrupt 326 self.interrupt = None 327 raise i 326 raise self.interrupt 328 327 329 328 def tick(self): … … 355 354 current = threading.currentThread() 356 355 for worker in self._workerThreads: 357 if worker is not current :356 if worker is not current and worker.isAlive: 358 357 worker.join() 359 358 trunk/cherrypy/server.py
r693 r698 32 32 """ 33 33 34 import warnings34 import cgi 35 35 import threading 36 36 import time 37 import warnings 37 38 38 39 import cherrypy … … 48 49 49 50 50 def start(initOnly=False, serverClass=None): 51 defaultOn = (cherrypy.config.get("server.environment") == "development") 52 if cherrypy.config.get('autoreload.on', defaultOn): 53 # Check initOnly. If True, we're probably not starting 54 # our own webserver, and therefore could do Very Bad Things 55 # when autoreload calls sys.exit. 56 if not initOnly: 51 _missing = object() 52 53 def start(initOnly=False, serverClass=_missing): 54 """Main function. MUST be called from the main thread. 55 56 Set initOnly to True to keep this function from blocking. 57 Set serverClass to None to skip starting any HTTP server. 58 """ 59 # This duplicates the line in start_app_server on purpose. 60 cherrypy._appserver_state = None 61 cherrypy._interrupt = None 62 63 conf = cherrypy.config.get 64 65 if serverClass is _missing: 66 serverClass = conf("server.class", _missing) 67 if serverClass is _missing: 68 import _cpwsgi 69 serverClass = _cpwsgi.WSGIServer 70 elif serverClass and isinstance(serverClass, basestring): 71 # Dynamically load the class from the given string 72 serverClass = cherrypy._cputil.attributes(serverClass) 73 74 # Autoreload, but check serverClass. If None, we're not starting 75 # our own webserver, and therefore could do Very Bad Things when 76 # autoreload calls sys.exit. 77 if serverClass is not None: 78 defaultOn = (conf("server.environment") == "development") 79 if conf('autoreload.on', defaultOn): 57 80 try: 58 81 autoreload.main(_start, (initOnly, serverClass)) … … 63 86 _start(initOnly, serverClass) 64 87 65 def _start(initOnly=False, serverClass=None): 66 """Main function.""" 88 def _start(initOnly, serverClass): 89 # This duplicates the line in start_app_server on purpose. 90 cherrypy._appserver_state = None 91 92 cherrypy._httpserverclass = serverClass 93 conf = cherrypy.config.get 94 67 95 try: 68 96 if cherrypy.codecoverage: … … 70 98 covercp.start() 71 99 72 # Use a flag to indicate the state of the cherrypy application server.73 # 0 = Not started74 # None = In process of starting75 # 1 = Started, ready to receive requests76 cherrypy._appserver_state = None77 78 100 # Output config options to log 79 if c herrypy.config.get("server.logConfigOptions", True):101 if conf("server.logConfigOptions", True): 80 102 cherrypy.config.outputConfigMap() 81 82 # Check the config options 83 # TODO 84 # config.checkConfigOptions() 103 # TODO: config.checkConfigOptions() 85 104 86 105 # If sessions are stored in files and we 87 106 # use threading, we need a lock on the file 88 if (c herrypy.config.get('server.threadPool') > 189 and c herrypy.config.get('session.storageType') == 'file'):107 if (conf('server.threadPool') > 1 108 and conf('session.storageType') == 'file'): 90 109 cherrypy._sessionFileLock = threading.RLock() 91 110 92 111 # set cgi.maxlen which will limit the size of POST request bodies 93 import cgi 94 cgi.maxlen = cherrypy.config.get('server.maxRequestSize') 95 96 # Call the functions from cherrypy.server.onStartServerList 97 for func in cherrypy.server.onStartServerList: 98 func() 112 cgi.maxlen = conf('server.maxRequestSize') 99 113 100 114 # Set up the profiler if requested. 101 if c herrypy.config.get("profiling.on", False):102 ppath = c herrypy.config.get("profiling.path", "")115 if conf("profiling.on", False): 116 ppath = conf("profiling.path", "") 103 117 cherrypy.profiler = profiler.Profiler(ppath) 104 118 else: 105 119 cherrypy.profiler = None 106 107 # Initi lize the built in filters120 121 # Initialize the built in filters 108 122 cherrypy._cputil._cpInitDefaultFilters() 109 123 cherrypy._cputil._cpInitUserDefinedFilters() 110 124 111 if initOnly: 112 cherrypy._appserver_state = 1 113 else: 114 run_server(serverClass) 115 except: 116 # _start may be called as the target of a Thread, in which case 117 # any errors would pass silently. Log them at least. 118 cherrypy.log(cherrypy._cputil.formatExc()) 119 raise 120 121 122 def run_server(serverClass=None): 123 """Prepare the requested server and then run it.""" 125 start_app_server() 126 start_http_server() 127 wait_until_ready() 128 129 if not initOnly: 130 # Block forever (wait for KeyboardInterrupt or SystemExit). 131 while True: 132 time.sleep(.1) 133 if cherrypy._interrupt: 134 raise cherrypy._interrupt 135 except KeyboardInterrupt: 136 cherrypy.log("<Ctrl-C> hit: shutting down server", "HTTP") 137 stop() 138 except SystemExit: 139 cherrypy.log("SystemExit raised: shutting down server", "HTTP") 140 stop() 141 142 def start_app_server(): 143 """Start the CherryPy core.""" 144 # Use a flag to indicate the state of the cherrypy application server. 145 # 0 = Not started 146 # None = In process of starting 147 # 1 = Started, ready to receive requests 148 cherrypy._appserver_state = None 149 150 # Call the functions from cherrypy.server.onStartServerList 151 for func in cherrypy.server.onStartServerList: 152 func() 153 154 cherrypy._appserver_state = 1 155 156 def start_http_server(serverClass=None): 157 """Start the requested HTTP server.""" 158 if serverClass is None: 159 serverClass = cherrypy._httpserverclass 160 if serverClass is None: 161 return 162 124 163 if cherrypy._httpserver is not None: 125 warnings.warn("You seem to have an HTTP server still running." 126 "Please call cherrypy.server.stop() before continuing.") 164 msg = ("You seem to have an HTTP server still running." 165 "Please call cherrypy.server.stop_http_server() " 166 "before continuing.") 167 warnings.warn(msg) 127 168 128 169 if cherrypy.config.get('server.socketPort'): … … 140 181 141 182 # Instantiate the server. 142 if serverClass is None:143 serverClass = cherrypy.config.get("server.class", None)144 if serverClass and isinstance(serverClass, basestring):145 serverClass = cherrypy._cputil.attributes(serverClass)146 if serverClass is None:147 import _cpwsgi148 serverClass = _cpwsgi.WSGIServer149 183 cherrypy._httpserver = serverClass() 150 184 151 # Start the http server. Must be done after wait_for_free_port (above). 152 # Note that _httpserver.start() will block this thread, so there 153 # isn't any notification in this thread that the HTTP server is 154 # truly ready. See wait_until_ready() for all the things that 155 # other threads should wait for before proceeding with requests. 156 try: 157 cherrypy._appserver_state = 1 158 # This should block until the http server stops. 159 cherrypy._httpserver.start() 160 except KeyboardInterrupt: 161 cherrypy.log("<Ctrl-C> hit: shutting down server", "HTTP") 162 stop() 163 except SystemExit: 164 cherrypy.log("SystemExit raised: shutting down server", "HTTP") 165 stop() 185 # HTTP servers MUST be started in a new thread, so that the 186 # main thread persists to receive KeyboardInterrupt's. This 187 # wrapper traps an interrupt in the http server's main thread 188 # and shutdowns CherryPy. 189 def _start_http(): 190 try: 191 cherrypy._httpserver.start() 192 except (KeyboardInterrupt, SystemExit), exc: 193 cherrypy._interrupt = exc 194 threading.Thread(target=_start_http).start() 166 195 167 196 … … 205 234 206 235 def stop(): 207 """Shutdown CherryPy (and any HTTP servers it started).""" 236 """Stop CherryPy and any HTTP servers it started.""" 237 stop_http_server() 238 stop_app_server() 239 240 def stop_app_server(): 241 """Stop CherryPy.""" 242 cherrypy._appserver_state = 0 243 cherrypy.log("CherryPy shut down", "HTTP") 244 245 def stop_http_server(): 246 """Stop the HTTP server.""" 208 247 try: 209 248 httpstop = cherrypy._httpserver.stop … … 211 250 pass 212 251 else: 213 # httpstop() shouldblock until the server is *truly* stopped.252 # httpstop() MUST block until the server is *truly* stopped. 214 253 httpstop() 215 254 cherrypy.log("HTTP Server shut down", "HTTP") … … 226 265 227 266 cherrypy._httpserver = None 228 cherrypy._appserver_state = 0229 cherrypy.log("CherryPy shut down", "HTTP")230 267 231 268 def restart(): 232 """Stop and start CherryPy.""" 233 http = getattr(cherrypy, '_httpserver', None) 269 """Restart CherryPy (and any HTTP servers it started).""" 234 270 stop() 235 if http: 236 # Start the server in a new thread 237 thread_args = {"serverClass": http.__class__} 238 t = threading.Thread(target=_start, kwargs=thread_args) 239 t.start() 240 else: 241 _start(initOnly=True) 271 start_app_server() 272 start_http_server() 242 273 wait_until_ready() 243 274 … … 245 276 """Block the caller until CherryPy is ready to receive requests.""" 246 277 278 # Wait for app to start up 247 279 while cherrypy._appserver_state != 1: 248 280 time.sleep(.1) 249 281 250 http = getattr(cherrypy, '_httpserver', None) 251 if http: 252 # Wait for HTTP server to start up 253 while not http.ready: 282 # Wait for HTTP server to start up 283 if cherrypy._httpserverclass is not None: 284 while not getattr(cherrypy._httpserver, "ready", None): 254 285 time.sleep(.1) 255 286 … … 299 330 cherrypy.log("Port %s not bound" % port, 'HTTP') 300 331 raise cherrypy.NotReady("Port not bound.") 332 333 def start_with_callback(func, args=None, kwargs=None, serverClass=_missing): 334 """Start CherryPy, then callback the given func in a new thread.""" 335 if args is None: 336 args = () 337 if kwargs is None: 338 kwargs = {} 339 args = (func,) + args 340 threading.Thread(target=_callback_intermediary, args=args, kwargs=kwargs).start() 341 cherrypy.server.start(serverClass=serverClass) 342 343 def _callback_intermediary(func, *args, **kwargs): 344 wait_until_ready() 345 func(*args, **kwargs) trunk/cherrypy/test/helper.py
r693 r698 46 46 47 47 import os, os.path 48 import sys 49 import time 48 import re 50 49 import socket 51 50 import StringIO 51 import sys 52 import thread 52 53 import threading 54 import time 55 import types 53 56 54 57 import cherrypy 55 58 import webtest 56 import types57 import re58 59 59 60 for _x in dir(cherrypy): … … 61 62 if isinstance(y, types.ClassType) and issubclass(y, cherrypy.Error): 62 63 webtest.ignored_exceptions.append(y) 63 64 65 def startServer(serverClass=None):66 """Start server in a new thread (same thread if serverClass is None)."""67 if serverClass is None:68 cherrypy.server.start(initOnly=True)69 else:70 t = threading.Thread(target=cherrypy.server.start,71 args=(False, serverClass))72 t.start()73 cherrypy.server.wait_until_ready()74 75 76 def stopServer():77 """Stop the current CP server."""78 cherrypy.server.stop()79 64 80 65 … … 87 72 88 73 class CPWebCase(webtest.WebCase): 74 75 def exit(self): 76 sys.exit() 89 77 90 78 def _getRequest(self, url, headers, method, body): … … 198 186 """ 199 187 setConfig(conf) 200 startServer(server) 188 cherrypy.server.start_with_callback(_run_test_suite_thread, 189 args=(moduleNames, conf), 190 serverClass=server) 191 192 def _run_test_suite_thread(moduleNames, conf): 193 cherrypy.server.wait_until_ready() 201 194 for testmod in moduleNames: 202 195 # Must run each module in a separate suite, … … 208 201 suite = CPTestLoader.loadTestsFromName(testmod) 209 202 CPTestRunner.run(suite) 210 stopServer() 211 203 thread.interrupt_main() 212 204 213 205 def testmain(server=None, conf=None): … … 216 208 conf = {} 217 209 setConfig(conf) 218 219 startServer(server) 210 cherrypy.server.start_with_callback(_test_main_thread, serverClass=server) 211 212 def _test_main_thread(): 213 cherrypy.server.wait_until_ready() 214 cherrypy._cputil._cpInitDefaultFilters() 220 215 try: 221 cherrypy._cputil._cpInitDefaultFilters()222 216 webtest.main() 223 217 finally: 224 # webtest.main == unittest.main, which raises SystemExit, 225 # so put stopServer in a finally clause 226 stopServer() 227 218 thread.interrupt_main() 219 trunk/cherrypy/test/test_noserver.py
r467 r698 26 26 27 27 cherrypy.config.update({"server.environment": "production"}) 28 cherrypy.server.start( initOnly = True)28 cherrypy.server.start(serverClass=None) 29 29 trunk/cherrypy/test/test_states.py
r696 r698 28 28 29 29 import time 30 import threading 30 31 31 32 import cherrypy … … 66 67 67 68 # Test server start 68 helper.startServer(self.serverClass)69 cherrypy.server.start(True, self.serverClass) 69 70 self.assertEqual(cherrypy._appserver_state, 1) 70 71 … … 78 79 79 80 # Test server stop 80 helper.stopServer()81 cherrypy.server.stop() 81 82 self.assertEqual(cherrypy._appserver_state, 0) 82 83 … … 84 85 self.assertRaises(cherrypy.NotReady, self.getPage, "/") 85 86 86 def test_1_KeyboardInterrupt(self): 87 if self.serverClass: 88 # Raise a keyboard interrupt in the HTTP server's main thread. 89 helper.startServer(self.serverClass) 90 cherrypy._httpserver.interrupt = KeyboardInterrupt 91 # Give the server time to shut down. 92 while cherrypy._appserver_state != 0: 93 time.sleep(.1) 94 self.assertEqual(cherrypy._httpserver, None) 95 self.assertEqual(cherrypy._appserver_state, 0) 96 self.assertRaises(cherrypy.NotReady, self.getPage, "/") 97 98 # Raise a keyboard interrupt in a page handler; on multithreaded 99 # servers, this should occur in one of the worker threads. 100 # This should raise a BadStatusLine error, since the worker 101 # thread will just die without writing a response. 102 from httplib import BadStatusLine 103 helper.startServer(self.serverClass) 104 self.assertRaises(BadStatusLine, self.getPage, "/ctrlc") 105 # Give the server time to shut down. 106 while cherrypy._appserver_state != 0: 107 print ".", 108 time.sleep(.1) 109 self.assertEqual(cherrypy._httpserver, None) 110 self.assertRaises(cherrypy.NotReady, self.getPage, "/") 111 112 def test_2_Restart(self): 87 def test_1_Restart(self): 113 88 # Test server start 114 helper.startServer(self.serverClass)89 cherrypy.server.start(True, self.serverClass) 115 90 self.getPage("/") 116 91 self.assertBody("Hello World") … … 127 102 self.assertBody("app was restarted succesfully") 128 103 129 # Now that we've restarted, test a KeyboardInterrupt (ticket 321). 104 cherrypy.server.stop() 105 self.assertEqual(cherrypy._appserver_state, 0) 106 107 def test_2_KeyboardInterrupt(self): 130 108 if self.serverClass: 131 cherrypy._httpserver.interrupt = KeyboardInterrupt 132 # Give the server time to shut down. 133 while cherrypy._appserver_state != 0: 134 time.sleep(.1) 109 110 # Raise a keyboard interrupt in the HTTP server's main thread. 111 def interrupt(): 112 cherrypy.server.wait_until_ready() 113 cherrypy._httpserver.interrupt = KeyboardInterrupt 114 threading.Thread(target=interrupt).start() 115 116 # We must start the server in this, the main thread 117 cherrypy.server.start(False, self.serverClass) 118 # Time passes... 135 119 self.assertEqual(cherrypy._httpserver, None) 120 self.assertEqual(cherrypy._appserver_state, 0) 121 self.assertRaises(cherrypy.NotReady, self.getPage, "/") 136 122 137 # Once the server has stopped, we should get a NotReady error again. 123 # Raise a keyboard interrupt in a page handler; on multithreaded 124 # servers, this should occur in one of the worker threads. 125 # This should raise a BadStatusLine error, since the worker 126 # thread will just die without writing a response. 127 def interrupt(): 128 cherrypy.server.wait_until_ready() 129 from httplib import BadStatusLine 130 self.assertRaises(BadStatusLine, self.getPage, "/ctrlc") 131 threading.Thread(target=interrupt).start() 132 133 cherrypy.server.start(False, self.serverClass) 134 # Time passes... 135 self.assertEqual(cherrypy._httpserver, None) 136 self.assertEqual(cherrypy._appserver_state, 0) 138 137 self.assertRaises(cherrypy.NotReady, self.getPage, "/") 139 140 138 141 139 142 140 def run(server, conf): 143 141 helper.setConfig(conf) 144 cherrypy._cputil._cpInitDefaultFilters()145 142 ServerStateTests.serverClass = server 146 143 suite = helper.CPTestLoader.loadTestsFromTestCase(ServerStateTests) … … 148 145 helper.CPTestRunner.run(suite) 149 146 finally: 150 helper.stopServer()147 cherrypy.server.stop() 151 148 152 149 … … 155 152 'server.socketPort': 8000, 156 153 'server.threadPool': 10, 157 'server.logToScreen': True,154 'server.logToScreen': False, 158 155 'server.logConfigOptions': False, 159 156 'server.environment': "production", … … 166 163 _run(None) 167 164 _run("cherrypy._cpwsgi.WSGIServer") 168 ## _run("cherrypy._cphttpserver.PooledThreadServer") 169 ## conf['server.threadPool'] = 1 170 ## _run("cherrypy._cphttpserver.CherryHTTPServer") 171 165 _run("cherrypy._cphttpserver.PooledThreadServer") 166 conf['server.threadPool'] = 1 167 _run("cherrypy._cphttpserver.CherryHTTPServer") trunk/cherrypy/test/test_tutorials.py
r690 r698 51 51 else: 52 52 module = __import__(target, globals(), locals(), ['']) 53 54 cherrypy.server.start(initOnly=True)55 53 56 54 def test01HelloWorld(self):

