Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/branches/cherrypy-2.x/cherrypy/_cpengine.py

Revision 1507 (checked in by fumanchu, 2 years ago)

2.x fix for #540 (signal handler). SIGTERM only in this branch (trunk does SIGHUP, too).

  • Property svn:eol-style set to native
Line 
1 """Create and manage the CherryPy application server engine."""
2
3 import cgi
4 import signal
5 import sys
6 import threading
7 import time
8 import warnings
9
10 import cherrypy
11 from cherrypy import _cphttptools, filters
12 from cherrypy.lib import autoreload, cptools
13
14 # Use a flag to indicate the state of the application server.
15 STOPPED = 0
16 STARTING = None
17 STARTED = 1
18
19
20 try:
21     if hasattr(signal, "SIGTERM"):
22         def SIGTERM(signum=None, frame=None):
23             cherrypy.server.stop()
24             cherrypy.engine.stop()
25         signal.signal(signal.SIGTERM, SIGTERM)
26 except ValueError, _signal_exc:
27     if _signal_exc.args[0] != "signal only works in main thread":
28         raise
29
30
31 class Engine(object):
32     """The application server engine, connecting HTTP servers to Requests."""
33    
34     request_class = _cphttptools.Request
35     response_class = _cphttptools.Response
36    
37     def __init__(self):
38         self.state = STOPPED
39        
40         self.seen_threads = {}
41         self.interrupt = None
42        
43         # Startup/shutdown hooks
44         self.on_start_server_list = []
45         self.on_stop_server_list = []
46         self.on_start_thread_list = []
47         self.on_stop_thread_list = []
48    
49     def setup(self):
50         # The only reason this method isn't in __init__ is so that
51         # "import cherrypy" can create an Engine() without a circular ref.
52         conf = cherrypy.config.get
53        
54         # Output config options to log
55         if conf("server.log_config_options", True):
56             cherrypy.config.outputConfigMap()
57        
58         # Hmmm...we *could* check config in _start instead, but I think
59         # most people would like CP to fail before autoreload kicks in.
60         err = cherrypy.WrongConfigValue
61         for name, section in cherrypy.config.configs.iteritems():
62             for k, v in section.iteritems():
63                 if k == "server.environment":
64                     if v and v not in cherrypy.config.environments:
65                         raise err("'%s' is not a registered environment." % v)
66        
67         if cherrypy.codecoverage:
68             from cherrypy.lib import covercp
69             covercp.start()
70        
71         # If sessions are stored in files and we
72         # use threading, we need a lock on the file
73         if (conf('server.thread_pool') > 1
74             and conf('session.storage_type') == 'file'):
75             cherrypy._sessionFileLock = threading.RLock()
76        
77         # Initialize the built in filters
78         filters.init()
79    
80     def start(self):
81         """Start the application server engine."""
82         self.state = STARTING
83         self.interrupt = None
84        
85         conf = cherrypy.config.get
86        
87         # Autoreload. Note that, if we're not starting our own HTTP server,
88         # autoreload could do Very Bad Things when it calls sys.exit, but
89         # deployers will just have to be educated and responsible for it.
90         if conf('autoreload.on', False):
91             try:
92                 freq = conf('autoreload.frequency', 1)
93                 autoreload.main(self._start, freq=freq)
94             except KeyboardInterrupt:
95                 cherrypy.log("<Ctrl-C> hit: shutting down autoreloader", "ENGINE")
96                 self.stop()
97             except SystemExit:
98                 cherrypy.log("SystemExit raised: shutting down autoreloader", "ENGINE")
99                 self.stop()
100                 # We must raise here: if this is a process spawned by
101                 # autoreload, then it must return its error code to
102                 # the parent.
103                 raise
104             return
105        
106         self._start()
107    
108     def _start(self):
109         for func in self.on_start_server_list:
110             func()
111         self.state = STARTED
112    
113     def block(self):
114         """Block forever (wait for stop(), KeyboardInterrupt or SystemExit)."""
115         try:
116             while self.state != STOPPED:
117                 time.sleep(.1)
118                 if self.interrupt:
119                     raise self.interrupt
120         except KeyboardInterrupt:
121             cherrypy.log("<Ctrl-C> hit: shutting down app server", "ENGINE")
122             self.stop()
123         except SystemExit:
124             cherrypy.log("SystemExit raised: shutting down app server", "ENGINE")
125             self.stop()
126             raise
127         except:
128             # Don't bother logging, since we're going to re-raise.
129             self.interrupt = sys.exc_info()[1]
130             self.stop()
131             raise
132    
133     def stop(self):
134         """Stop the application server engine."""
135         if self.state != STOPPED:
136             for thread_ident, i in self.seen_threads.iteritems():
137                 for func in self.on_stop_thread_list:
138                     func(i)
139             self.seen_threads.clear()
140            
141             for func in self.on_stop_server_list:
142                 func()
143            
144             self.state = STOPPED
145             cherrypy.log("CherryPy shut down", "ENGINE")
146    
147     def restart(self):
148         """Restart the application server engine."""
149         self.stop()
150         self.start()
151    
152     def wait(self):
153         """Block the caller until ready to receive requests (or error)."""
154         while not self.ready:
155             time.sleep(.1)
156             if self.interrupt:
157                 msg = "The CherryPy application server errored"
158                 raise cherrypy.NotReady(msg, "ENGINE")
159    
160     def _is_ready(self):
161         return bool(self.state == STARTED)
162     ready = property(_is_ready, doc="Return True if the server is ready to"
163                                     " receive requests, False otherwise.")
164    
165     def request(self, clientAddress, remoteHost, scheme="http"):
166         """Obtain an HTTP Request object.
167         
168         clientAddress: the (IP address, port) of the client
169         remoteHost: should be the client's host name. If not available
170             (because no reverse DNS lookup is performed), the client
171             IP should be provided.
172         scheme: either "http" or "https"; defaults to "http"
173         """
174         if self.state == STOPPED:
175             raise cherrypy.NotReady("The CherryPy server has stopped.")
176         elif self.state == STARTING:
177             raise cherrypy.NotReady("The CherryPy server could not start.")
178        
179         threadID = threading._get_ident()
180         if threadID not in self.seen_threads:
181            
182             if cherrypy.codecoverage:
183                 from cherrypy.lib import covercp
184                 covercp.start()
185            
186             i = len(self.seen_threads) + 1
187             self.seen_threads[threadID] = i
188            
189             for func in self.on_start_thread_list:
190                 func(i)
191        
192         r = self.request_class(clientAddress[0], clientAddress[1],
193                                remoteHost, scheme)
194         cherrypy.serving.request = r
195         cherrypy.serving.response = self.response_class()
196         return r
197
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets