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

root/branches/cherrypy-2.1/cherrypy/_cpserver.py

Revision 718 (checked in by fumanchu, 3 years ago)

Initial fix for #333.

Line 
1 """
2 Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7
8     * Redistributions of source code must retain the above copyright notice,
9       this list of conditions and the following disclaimer.
10     * Redistributions in binary form must reproduce the above copyright notice,
11       this list of conditions and the following disclaimer in the documentation
12       and/or other materials provided with the distribution.
13     * Neither the name of the CherryPy Team nor the names of its contributors
14       may be used to endorse or promote products derived from this software
15       without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 """
28
29 """
30 Main CherryPy module:
31     - Creates a server
32 """
33
34 import cgi
35 import threading
36 import time
37 import warnings
38
39 import cherrypy
40 from cherrypy import _cphttptools
41 from cherrypy.lib import autoreload, profiler
42
43 # Use a flag to indicate the state of the application server.
44 STOPPED = 0
45 STARTING = None
46 STARTED = 1
47
48 seen_threads = {}
49
50 _missing = object()
51
52
53 class Server(object):
54    
55     def __init__(self):
56         self.state = STOPPED
57        
58         self.httpserver = None
59         self.httpserverclass = None
60         self.interrupt = None
61        
62         # Set some special attributes for adding hooks
63         self.onStartServerList = []
64         self.onStartThreadList = []
65         self.onStopServerList = []
66         self.onStopThreadList = []
67    
68     def start(self, initOnly=False, serverClass=_missing):
69         """Main function. MUST be called from the main thread.
70         
71         Set initOnly to True to keep this function from blocking.
72         Set serverClass to None to skip starting any HTTP server.
73         """
74         self.state = STARTING
75         self.interrupt = None
76        
77         conf = cherrypy.config.get
78        
79         if serverClass is _missing:
80             serverClass = conf("server.class", _missing)
81         if serverClass is _missing:
82             import _cpwsgi
83             serverClass = _cpwsgi.WSGIServer
84         elif serverClass and isinstance(serverClass, basestring):
85             # Dynamically load the class from the given string
86             serverClass = cherrypy._cputil.attributes(serverClass)
87        
88         self.blocking = not initOnly
89         self.httpserverclass = serverClass
90        
91         # Autoreload, but check serverClass. If None, we're not starting
92         # our own webserver, and therefore could do Very Bad Things when
93         # autoreload calls sys.exit.
94         if serverClass is not None:
95             defaultOn = (conf("server.environment") == "development")
96             if conf('autoreload.on', defaultOn):
97                 try:
98                     autoreload.main(self._start)
99                 except KeyboardInterrupt:
100                     cherrypy.log("<Ctrl-C> hit: shutting down autoreloader", "HTTP")
101                     self.stop()
102                 except SystemExit:
103                     cherrypy.log("SystemExit raised: shutting down autoreloader", "HTTP")
104                     self.stop()
105                     # We must raise here: if this is a process spawned by
106                     # autoreload, then it must return its error code to
107                     # the parent.
108                     raise
109                 return
110        
111         self._start()
112    
113     def _start(self):
114         # Output config options to log
115         if cherrypy.config.get("server.logConfigOptions", True):
116             cherrypy.config.outputConfigMap()
117        
118         try:
119             configure()
120            
121             for func in cherrypy.server.onStartServerList:
122                 func()
123             self.start_http_server()
124             self.state = STARTED
125            
126             if self.blocking:
127                 # Block forever (wait for KeyboardInterrupt or SystemExit).
128                 while True:
129                     time.sleep(.1)
130                     if self.interrupt:
131                         raise self.interrupt
132         except KeyboardInterrupt:
133             cherrypy.log("<Ctrl-C> hit: shutting down server", "HTTP")
134             self.stop()
135         except SystemExit:
136             cherrypy.log("SystemExit raised: shutting down server", "HTTP")
137             self.stop()
138    
139     def start_http_server(self, blocking=True):
140         """Start the requested HTTP server."""
141         if self.httpserver is not None:
142             msg = ("You seem to have an HTTP server still running."
143                    "Please call server.stop_http_server() "
144                    "before continuing.")
145             warnings.warn(msg)
146        
147         if self.httpserverclass is None:
148             return
149        
150         if cherrypy.config.get('server.socketPort'):
151             host = cherrypy.config.get('server.socketHost')
152             port = cherrypy.config.get('server.socketPort')
153            
154             wait_for_free_port(host, port)
155            
156             if not host:
157                 host = 'localhost'
158             onWhat = "http://%s:%s/" % (host, port)
159         else:
160             onWhat = "socket file: %s" % cherrypy.config.get('server.socketFile')
161        
162         # Instantiate the server.
163         self.httpserver = self.httpserverclass()
164        
165         # HTTP servers MUST be started in a new thread, so that the
166         # main thread persists to receive KeyboardInterrupt's. This
167         # wrapper traps an interrupt in the http server's main thread
168         # and shutdowns CherryPy.
169         def _start_http():
170             try:
171                 self.httpserver.start()
172             except (KeyboardInterrupt, SystemExit), exc:
173                 self.interrupt = exc
174         threading.Thread(target=_start_http).start()
175        
176         if blocking:
177             self.wait_for_http_ready()
178        
179         cherrypy.log("Serving HTTP on %s" % onWhat, 'HTTP')
180    
181     def wait_for_http_ready(self):
182         if self.httpserverclass is not None:
183             while not getattr(self.httpserver, "ready", True):
184                 time.sleep(.1)
185            
186             # Wait for port to be occupied
187             if cherrypy.config.get('server.socketPort'):
188                 host = cherrypy.config.get('server.socketHost')
189                 port = cherrypy.config.get('server.socketPort')
190                 wait_for_occupied_port(host, port)
191    
192     def request(self, clientAddress, remoteHost, requestLine,
193                 headers, rfile, scheme="http"):
194         """request(clientAddress, remoteHost, requestLine, headers, rfile, scheme="http")
195         
196         clientAddress: the (IP address, port) of the client
197         remoteHost: the IP address of the client
198         requestLine: "<HTTP method> <url?qs> HTTP/<version>",
199                 e.g. "GET /main?abc=123 HTTP/1.1"
200         headers: a list of (key, value) tuples
201         rfile: a file-like object from which to read the HTTP request body
202         scheme: either "http" or "https"; defaults to "http"
203         """
204         if self.state == STOPPED:
205             raise cherrypy.NotReady("The CherryPy server has stopped.")
206         elif self.state == STARTING:
207             raise cherrypy.NotReady("The CherryPy server could not start.")
208        
209         threadID = threading._get_ident()
210         if threadID not in seen_threads:
211            
212             if cherrypy.codecoverage:
213                 from cherrypy.lib import covercp
214                 covercp.start()
215            
216             i = len(seen_threads) + 1
217             seen_threads[threadID] = i
218            
219             for func in self.onStartThreadList:
220                 func(i)
221        
222         if cherrypy.profiler:
223             cherrypy.profiler.run(_cphttptools.Request, clientAddress, remoteHost,
224                                   requestLine, headers, rfile, scheme)
225         else:
226             _cphttptools.Request(clientAddress, remoteHost,
227                                  requestLine, headers, rfile, scheme)
228    
229     def stop(self):
230         """Stop, including any HTTP servers."""
231         self.stop_http_server()
232        
233         for thread_ident, i in seen_threads.iteritems():
234             for func in self.onStopThreadList:
235                 func(i)
236         seen_threads.clear()
237        
238         for func in self.onStopServerList:
239             func()
240        
241         self.state = STOPPED
242         cherrypy.log("CherryPy shut down", "HTTP")
243    
244     def stop_http_server(self):
245         """Stop the HTTP server."""
246         try:
247             httpstop = self.httpserver.stop
248         except AttributeError:
249             pass
250         else:
251             # httpstop() MUST block until the server is *truly* stopped.
252             httpstop()
253             cherrypy.log("HTTP Server shut down", "HTTP")
254        
255         self.httpserver = None
256    
257     def restart(self):
258         """Restart, including any HTTP servers."""
259         self.stop()
260         for func in self.onStartServerList:
261             func()
262         self.start_http_server()
263         self.state = STARTED
264    
265     def wait(self):
266         """Block the caller until ready to receive requests."""
267         while not self.ready:
268             time.sleep(.1)
269    
270     def _is_ready(self):
271         return bool(self.state == STARTED)
272     ready = property(_is_ready, doc="Return True if the server is ready to receive requests, False otherwise.")
273    
274     def start_with_callback(self, func, args=None, kwargs=None,
275                             serverClass=_missing):
276         """Start, then callback the given func in a new thread."""
277         if args is None:
278             args = ()
279         if kwargs is None:
280             kwargs = {}
281         args = (func,) + args
282        
283         def _callback(func, *args, **kwargs):
284             self.wait()
285             func(*args, **kwargs)
286         threading.Thread(target=_callback, args=args, kwargs=kwargs).start()
287        
288         self.start(serverClass=serverClass)
289
290
291 def configure():
292     """Perform one-time actions to prepare the CherryPy core."""
293     if cherrypy.codecoverage:
294         from cherrypy.lib import covercp
295         covercp.start()
296    
297     conf = cherrypy.config.get
298     # TODO: config.checkConfigOptions()
299    
300     # If sessions are stored in files and we
301     # use threading, we need a lock on the file
302     if (conf('server.threadPool') > 1
303         and conf('session.storageType') == 'file'):
304         cherrypy._sessionFileLock = threading.RLock()
305    
306     # set cgi.maxlen which will limit the size of POST request bodies
307     cgi.maxlen = conf('server.maxRequestSize')
308    
309     # Set up the profiler if requested.
310     if conf("profiling.on", False):
311         ppath = conf("profiling.path", "")
312         cherrypy.profiler = profiler.Profiler(ppath)
313     else:
314         cherrypy.profiler = None
315    
316     # Initialize the built in filters
317     cherrypy._cputil._cpInitDefaultFilters()
318     cherrypy._cputil._cpInitUserDefinedFilters()
319
320
321 def check_port(host, port):
322     """Raise an error if the given port is not free on the given host."""
323     if not host:
324         host = 'localhost'
325    
326     import socket
327     try:
328         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
329         s.connect((host, int(port)))
330         s.close()
331         raise IOError("Port %s is in use on %s; perhaps the previous "
332                       "server did not shut down properly." %
333                       (repr(port), repr(host)))
334     except socket.error:
335         pass
336
337 def wait_for_free_port(host, port):
338     """Wait for the specified port to become free (drop requests)."""
339     if not host:
340         host = 'localhost'
341    
342     for trial in xrange(50):
343         try:
344             check_port(host, port)
345         except IOError:
346             # Give the old server thread time to free the port.
347             time.sleep(.1)
348         else:
349             return
350    
351     cherrypy.log("Port %s not free on %s" % (repr(port), repr(host)), 'HTTP')
352     raise cherrypy.NotReady("Port not free.")
353
354 def wait_for_occupied_port(host, port):
355     """Wait for the specified port to become active (receive requests)."""
356     if not host:
357         host = 'localhost'
358    
359     for trial in xrange(50):
360         try:
361             check_port(host, port)
362         except IOError:
363             return
364         else:
365             time.sleep(.1)
366    
367     cherrypy.log("Port %s not bound on %s" % (repr(port), repr(host)), 'HTTP')
368     raise cherrypy.NotReady("Port not bound.")
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets