diff options
Diffstat (limited to 'cherrypy/_cpengine.py')
-rw-r--r-- | cherrypy/_cpengine.py | 380 |
1 files changed, 0 insertions, 380 deletions
diff --git a/cherrypy/_cpengine.py b/cherrypy/_cpengine.py deleted file mode 100644 index 6c58b877..00000000 --- a/cherrypy/_cpengine.py +++ /dev/null @@ -1,380 +0,0 @@ -"""Create and manage the CherryPy application engine.""" - -import cgi -import os -import re -import signal -import sys -import threading -import time - -import cherrypy -from cherrypy import _cprequest - -# Use a flag to indicate the state of the application engine. -STOPPED = 0 -STARTING = None -STARTED = 1 - - -class PerpetualTimer(threading._Timer): - - def run(self): - while True: - self.finished.wait(self.interval) - if self.finished.isSet(): - return - self.function(*self.args, **self.kwargs) - - -class Engine(object): - """Interface for (HTTP) applications, plus process controls. - - Servers and gateways should not instantiate Request objects directly. - Instead, they should ask an Engine object for a request via the - Engine.request method. - - Blocking is completely optional! The Engine's blocking, signal and - interrupt handling, privilege dropping, and autoreload features are - not a good idea when driving CherryPy applications from another - deployment tool (but an Engine is a great deployment tool itself). - By calling start(blocking=False), you avoid blocking and interrupt- - handling issues. By setting Engine.SIGHUP and Engine.SIGTERM to None, - you can completely disable the signal handling (and therefore disable - autoreloads triggered by SIGHUP). Set Engine.autoreload_on to False - to disable autoreload entirely. - """ - - # Configurable attributes - request_class = _cprequest.Request - response_class = _cprequest.Response - deadlock_poll_freq = 60 - autoreload_on = True - autoreload_frequency = 1 - autoreload_match = ".*" - - def __init__(self): - self.state = STOPPED - - # Startup/shutdown hooks - self.on_start_engine_list = [] - self.on_stop_engine_list = [] - self.on_start_thread_list = [] - self.on_stop_thread_list = [] - self.seen_threads = {} - - self.servings = [] - - self.mtimes = {} - self.reload_files = [] - - self.monitor_thread = None - - def start(self, blocking=True): - """Start the application engine.""" - self.state = STARTING - - cherrypy.checker() - - for func in self.on_start_engine_list: - func() - - self.state = STARTED - - self._set_signals() - - freq = self.deadlock_poll_freq - if freq > 0: - self.monitor_thread = PerpetualTimer(freq, self.monitor) - self.monitor_thread.setName("CPEngine Monitor") - self.monitor_thread.start() - - if blocking: - self.block() - - def block(self): - """Block forever (wait for stop(), KeyboardInterrupt or SystemExit).""" - try: - while self.state != STOPPED: - # Note that autoreload_frequency controls - # sleep timer even if autoreload is off. - time.sleep(self.autoreload_frequency) - if self.autoreload_on: - self.autoreload() - except KeyboardInterrupt: - cherrypy.log("<Ctrl-C> hit: shutting down app engine", "ENGINE") - cherrypy.server.stop() - self.stop() - except SystemExit: - cherrypy.log("SystemExit raised: shutting down app engine", "ENGINE") - cherrypy.server.stop() - self.stop() - raise - except: - # Don't bother logging, since we're going to re-raise. - # Note that we don't stop the HTTP server here. - self.stop() - raise - - def reexec(self): - """Re-execute the current process.""" - cherrypy.server.stop() - self.stop() - - args = sys.argv[:] - cherrypy.log("Re-spawning %s" % " ".join(args), "ENGINE") - args.insert(0, sys.executable) - - if sys.platform == "win32": - args = ['"%s"' % arg for arg in args] - - # Some platforms (OS X) will error if all threads are not - # ABSOLUTELY terminated. See http://www.cherrypy.org/ticket/581. - for trial in xrange(self.reexec_retry * 10): - try: - os.execv(sys.executable, args) - return - except OSError, x: - if x.errno != 45: - raise - time.sleep(0.1) - else: - raise - - # Number of seconds to retry reexec if os.execv fails. - reexec_retry = 2 - - def autoreload(self): - """Reload the process if registered files have been modified.""" - sysfiles = [] - for k, m in sys.modules.items(): - if re.match(self.autoreload_match, k): - if hasattr(m, "__loader__"): - if hasattr(m.__loader__, "archive"): - k = m.__loader__.archive - k = getattr(m, "__file__", None) - sysfiles.append(k) - - for filename in sysfiles + self.reload_files: - if filename: - if filename.endswith(".pyc"): - filename = filename[:-1] - - oldtime = self.mtimes.get(filename, 0) - if oldtime is None: - # Module with no .py file. Skip it. - continue - - try: - mtime = os.stat(filename).st_mtime - except OSError: - # Either a module with no .py file, or it's been deleted. - mtime = None - - if filename not in self.mtimes: - # If a module has no .py file, this will be None. - self.mtimes[filename] = mtime - else: - if mtime is None or mtime > oldtime: - # The file has been deleted or modified. - self.reexec() - - def stop(self): - """Stop the application 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_engine_list: - func() - - if self.monitor_thread: - self.monitor_thread.cancel() - self.monitor_thread.join() - self.monitor_thread = None - - self.state = STOPPED - cherrypy.log("CherryPy shut down", "ENGINE") - - def restart(self): - """Restart the application engine (does not block).""" - self.stop() - self.start(blocking=False) - - def wait(self): - """Block the caller until ready to receive requests (or error).""" - while not (self.state == STARTED): - time.sleep(.1) - - def request(self, local_host, remote_host, scheme="http", - server_protocol="HTTP/1.1"): - """Obtain and return an HTTP Request object. (Core) - - local_host should be an http.Host object with the server info. - remote_host should be an http.Host object with the client info. - scheme: either "http" or "https"; defaults to "http" - """ - if self.state == STOPPED: - req = NotReadyRequest("The CherryPy engine has stopped.") - elif self.state == STARTING: - req = NotReadyRequest("The CherryPy engine could not start.") - else: - # Only run on_start_thread_list if the engine is running. - threadID = threading._get_ident() - if threadID not in self.seen_threads: - i = len(self.seen_threads) + 1 - self.seen_threads[threadID] = i - - for func in self.on_start_thread_list: - func(i) - req = self.request_class(local_host, remote_host, scheme, - server_protocol) - resp = self.response_class() - cherrypy.serving.load(req, resp) - self.servings.append((req, resp)) - return req - - def release(self): - """Close and de-reference the current request and response. (Core)""" - req = cherrypy.serving.request - - try: - req.close() - except: - cherrypy.log(traceback=True) - - try: - self.servings.remove((req, cherrypy.serving.response)) - except ValueError: - pass - - cherrypy.serving.clear() - - def monitor(self): - """Check timeout on all responses. (Internal)""" - if self.state == STARTED: - for req, resp in self.servings: - resp.check_timeout() - - def start_with_callback(self, func, args=None, kwargs=None): - """Start the given func in a new thread, then start self and block.""" - - if args is None: - args = () - if kwargs is None: - kwargs = {} - args = (func,) + args - - def _callback(func, *a, **kw): - self.wait() - func(*a, **kw) - t = threading.Thread(target=_callback, args=args, kwargs=kwargs) - t.setName("CPEngine Callback " + t.getName()) - t.start() - - self.start() - - - # Signal handling # - - SIGHUP = None - SIGTERM = None - - if hasattr(signal, "SIGHUP"): - def SIGHUP(self, signum=None, frame=None): - self.reexec() - - if hasattr(signal, "SIGTERM"): - def SIGTERM(signum=None, frame=None): - cherrypy.server.stop() - self.stop() - - def _set_signals(self): - if self.SIGHUP: - signal.signal(signal.SIGHUP, self.SIGHUP) - if self.SIGTERM: - signal.signal(signal.SIGTERM, self.SIGTERM) - - - # Drop privileges # - - # Special thanks to Gavin Baker: http://antonym.org/node/100. - try: - import pwd, grp - except ImportError: - try: - os.umask - except AttributeError: - def drop_privileges(self): - """Drop privileges. Not implemented on this platform.""" - raise NotImplementedError - else: - umask = None - - def drop_privileges(self): - """Drop privileges. Windows version (umask only).""" - if self.umask is not None: - old_umask = os.umask(self.umask) - cherrypy.log('umask old: %03o, new: %03o' % - (old_umask, self.umask), "PRIV") - else: - uid = None - gid = None - umask = None - - def drop_privileges(self): - """Drop privileges. UNIX version (uid, gid, and umask).""" - if not (self.uid is None and self.gid is None): - if self.uid is None: - uid = None - elif isinstance(self.uid, basestring): - uid = self.pwd.getpwnam(self.uid)[2] - else: - uid = self.uid - - if self.gid is None: - gid = None - elif isinstance(self.gid, basestring): - gid = self.grp.getgrnam(self.gid)[2] - else: - gid = self.gid - - def names(): - name = self.pwd.getpwuid(os.getuid())[0] - group = self.grp.getgrgid(os.getgid())[0] - return name, group - - cherrypy.log('Started as %r/%r' % names(), "PRIV") - if gid is not None: - os.setgid(gid) - if uid is not None: - os.setuid(uid) - cherrypy.log('Running as %r/%r' % names(), "PRIV") - - if self.umask is not None: - old_umask = os.umask(self.umask) - cherrypy.log('umask old: %03o, new: %03o' % - (old_umask, self.umask), "PRIV") - - -class NotReadyRequest: - - throw_errors = True - show_tracebacks = True - error_page = {} - - def __init__(self, msg): - self.msg = msg - self.protocol = (1,1) - - def close(self): - pass - - 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 - |