"""Create and manage the CherryPy application server engine.""" import cgi import signal import sys import threading import time import warnings import cherrypy from cherrypy import _cphttptools, filters from cherrypy.lib import autoreload, cptools # Use a flag to indicate the state of the application server. STOPPED = 0 STARTING = None STARTED = 1 try: if hasattr(signal, "SIGTERM"): def SIGTERM(signum=None, frame=None): cherrypy.server.stop() cherrypy.engine.stop() signal.signal(signal.SIGTERM, SIGTERM) except ValueError, _signal_exc: if _signal_exc.args[0] != "signal only works in main thread": raise class Engine(object): """The application server engine, connecting HTTP servers to Requests.""" request_class = _cphttptools.Request response_class = _cphttptools.Response def __init__(self): self.state = STOPPED self.seen_threads = {} self.interrupt = None # Startup/shutdown hooks self.on_start_server_list = [] self.on_stop_server_list = [] self.on_start_thread_list = [] self.on_stop_thread_list = [] def setup(self): # The only reason this method isn't in __init__ is so that # "import cherrypy" can create an Engine() without a circular ref. conf = cherrypy.config.get # Output config options to log if conf("server.log_config_options", True): cherrypy.config.outputConfigMap() # Hmmm...we *could* check config in _start instead, but I think # most people would like CP to fail before autoreload kicks in. err = cherrypy.WrongConfigValue for name, section in cherrypy.config.configs.iteritems(): for k, v in section.iteritems(): if k == "server.environment": if v and v not in cherrypy.config.environments: raise err("'%s' is not a registered environment." % v) if cherrypy.codecoverage: from cherrypy.lib import covercp covercp.start() # If sessions are stored in files and we # use threading, we need a lock on the file if (conf('server.thread_pool') > 1 and conf('session.storage_type') == 'file'): cherrypy._sessionFileLock = threading.RLock() # Initialize the built in filters filters.init() def start(self): """Start the application server engine.""" self.state = STARTING self.interrupt = None conf = cherrypy.config.get # Autoreload. Note that, if we're not starting our own HTTP server, # autoreload could do Very Bad Things when it calls sys.exit, but # deployers will just have to be educated and responsible for it. if conf('autoreload.on', False): try: freq = conf('autoreload.frequency', 1) autoreload.main(self._start, freq=freq) except KeyboardInterrupt: cherrypy.log(" hit: shutting down autoreloader", "ENGINE") self.stop() except SystemExit: cherrypy.log("SystemExit raised: shutting down autoreloader", "ENGINE") self.stop() # We must raise here: if this is a process spawned by # autoreload, then it must return its error code to # the parent. raise return self._start() def _start(self): for func in self.on_start_server_list: func() self.state = STARTED def block(self): """Block forever (wait for stop(), KeyboardInterrupt or SystemExit).""" try: while self.state != STOPPED: time.sleep(.1) if self.interrupt: raise self.interrupt except KeyboardInterrupt: cherrypy.log(" hit: shutting down app server", "ENGINE") self.stop() except SystemExit: cherrypy.log("SystemExit raised: shutting down app server", "ENGINE") self.stop() raise except: # Don't bother logging, since we're going to re-raise. self.interrupt = sys.exc_info()[1] self.stop() raise def stop(self): """Stop the application server engine.""" if self.state != STOPPED: for thread_ident, i in self.seen_threads.iteritems(): for func in self.on_stop_thread_list: func(i) self.seen_threads.clear() for func in self.on_stop_server_list: func() self.state = STOPPED cherrypy.log("CherryPy shut down", "ENGINE") def restart(self): """Restart the application server engine.""" self.stop() self.start() def wait(self): """Block the caller until ready to receive requests (or error).""" while not self.ready: time.sleep(.1) if self.interrupt: msg = "The CherryPy application server errored" raise cherrypy.NotReady(msg, "ENGINE") def _is_ready(self): return bool(self.state == STARTED) ready = property(_is_ready, doc="Return True if the server is ready to" " receive requests, False otherwise.") def request(self, clientAddress, remoteHost, scheme="http"): """Obtain an HTTP Request object. clientAddress: the (IP address, port) of the client remoteHost: should be the client's host name. If not available (because no reverse DNS lookup is performed), the client IP should be provided. scheme: either "http" or "https"; defaults to "http" """ if self.state == STOPPED: raise cherrypy.NotReady("The CherryPy server has stopped.") elif self.state == STARTING: raise cherrypy.NotReady("The CherryPy server could not start.") threadID = threading._get_ident() if threadID not in self.seen_threads: if cherrypy.codecoverage: from cherrypy.lib import covercp covercp.start() i = len(self.seen_threads) + 1 self.seen_threads[threadID] = i for func in self.on_start_thread_list: func(i) r = self.request_class(clientAddress[0], clientAddress[1], remoteHost, scheme) cherrypy.serving.request = r cherrypy.serving.response = self.response_class() return r