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

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

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

Fix for #560 (misleading log on socket_host="") for CP 2.2 and CP 3.

  • Property svn:eol-style set to native
Line 
1 """Create and manage the CherryPy server."""
2
3 import threading
4 import time
5
6 import cherrypy
7 from cherrypy import _cphttptools
8 from cherrypy.lib import cptools
9
10 from cherrypy._cpengine import Engine, STOPPED, STARTING, STARTED
11
12 _missing = object()
13
14
15 class Server(Engine):
16    
17     def __init__(self):
18         Engine.__init__(self)
19         self._is_setup = False
20         self.blocking = True
21        
22         self.httpserver = None
23         # Starting in 2.2, the "httpserverclass" attr is essentially dead;
24         # no CP code uses it. Inspect "httpserver" instead.
25         self.httpserverclass = None
26        
27         # Backward compatibility:
28         self.onStopServerList = self.on_stop_server_list
29         self.onStartThreadList = self.on_start_thread_list
30         self.onStartServerList = self.on_start_server_list
31         self.onStopThreadList = self.on_stop_thread_list
32    
33     def start(self, init_only=False, server_class=_missing, server=None, **kwargs):
34         """Main function. MUST be called from the main thread.
35         
36         Set initOnly to True to keep this function from blocking.
37         Set serverClass and server to None to skip starting any HTTP server.
38         """
39        
40         # Read old variable names for backward compatibility
41         if 'initOnly' in kwargs:
42             init_only = kwargs['initOnly']
43         if 'serverClass' in kwargs:
44             server_class = kwargs['serverClass']
45        
46         conf = cherrypy.config.get
47        
48         if not init_only:
49             init_only = conf('server.init_only', False)
50        
51         if server is None:
52             server = conf('server.instance', None)
53         if server is None:
54             if server_class is _missing:
55                 server_class = conf("server.class", _missing)
56             if server_class is _missing:
57                 import _cpwsgi
58                 server_class = _cpwsgi.WSGIServer
59             elif server_class and isinstance(server_class, basestring):
60                 # Dynamically load the class from the given string
61                 server_class = cptools.attributes(server_class)
62             self.httpserverclass = server_class
63             if server_class is not None:
64                 self.httpserver = server_class()
65         else:
66             if isinstance(server, basestring):
67                 server = cptools.attributes(server)
68             self.httpserverclass = server.__class__
69             self.httpserver = server
70        
71         self.blocking = not init_only
72         Engine.start(self)
73    
74     def _start(self):
75         if not self._is_setup:
76             self.setup()
77             self._is_setup = True
78         Engine._start(self)
79         self.start_http_server()
80         if self.blocking:
81             self.block()
82    
83     def restart(self):
84         """Restart the application server engine."""
85         self.stop()
86         self.state = STARTING
87         self.interrupt = None
88         self._start()
89    
90     def start_http_server(self, blocking=True):
91         """Start the requested HTTP server."""
92         if not self.httpserver:
93             return
94        
95         if cherrypy.config.get('server.socket_port'):
96             host = cherrypy.config.get('server.socket_host')
97             port = cherrypy.config.get('server.socket_port')
98            
99             wait_for_free_port(host, port)
100            
101             if not host:
102                 host = '0.0.0.0'
103             on_what = "http://%s:%s/" % (host, port)
104         else:
105             on_what = "socket file: %s" % cherrypy.config.get('server.socket_file')
106        
107         # HTTP servers MUST be started in a new thread, so that the
108         # main thread persists to receive KeyboardInterrupt's. If an
109         # exception is raised in the http server's main thread then it's
110         # trapped here, and the CherryPy app server is shut down (via
111         # self.interrupt).
112         def _start_http():
113             try:
114                 self.httpserver.start()
115             except KeyboardInterrupt, exc:
116                 self.interrupt = exc
117                 self.stop()
118             except SystemExit, exc:
119                 self.interrupt = exc
120                 self.stop()
121                 raise
122         t = threading.Thread(target=_start_http)
123         t.setName("CPHTTPServer " + t.getName())
124         t.start()
125        
126         if blocking:
127             self.wait_for_http_ready()
128        
129         cherrypy.log("Serving HTTP on %s" % on_what, 'HTTP')
130    
131     def wait(self):
132         """Block the caller until ready to receive requests (or error)."""
133         Engine.wait(self)
134         self.wait_for_http_ready()
135    
136     def wait_for_http_ready(self):
137         if self.httpserver:
138             while (not getattr(self.httpserver, "ready", True)
139                    and not self.interrupt
140                    and self.state != STOPPED):
141                 time.sleep(.1)
142            
143             # Wait for port to be occupied
144             if cherrypy.config.get('server.socket_port'):
145                 host = cherrypy.config.get('server.socket_host')
146                 port = cherrypy.config.get('server.socket_port')
147                 if not host:
148                     host = 'localhost'
149                
150                 for trial in xrange(50):
151                     if self.interrupt:
152                         break
153                     try:
154                         check_port(host, port)
155                     except IOError:
156                         break
157                     else:
158                         time.sleep(.1)
159                 else:
160                     cherrypy.log("Port %s not bound on %s" %
161                                  (repr(port), repr(host)), 'HTTP')
162                     raise cherrypy.NotReady("Port not bound.")
163    
164     def stop(self):
165         """Stop, including any HTTP servers."""
166         self.stop_http_server()
167         Engine.stop(self)
168    
169     def stop_http_server(self):
170         """Stop the HTTP server."""
171         try:
172             httpstop = self.httpserver.stop
173         except AttributeError:
174             pass
175         else:
176             # httpstop() MUST block until the server is *truly* stopped.
177             httpstop()
178             cherrypy.log("HTTP Server shut down", "HTTP")
179    
180     def start_with_callback(self, func, args=None, kwargs=None,
181                             server_class = _missing, serverClass = None):
182         """Start, then callback the given func in a new thread."""
183        
184         # Read old name for backward compatibility
185         if serverClass is not None:
186             server_class = None
187        
188         if args is None:
189             args = ()
190         if kwargs is None:
191             kwargs = {}
192         args = (func,) + args
193        
194         def _callback(func, *args, **kwargs):
195             self.wait()
196             func(*args, **kwargs)
197         t = threading.Thread(target=_callback, args=args, kwargs=kwargs)
198         t.setName("CPServer Callback " + t.getName())
199         t.start()
200        
201         self.start(server_class = server_class)
202
203
204 def check_port(host, port):
205     """Raise an error if the given port is not free on the given host."""
206     sock_file = cherrypy.config.get('server.socket_file')
207     if sock_file:
208         return
209    
210     if not host:
211         host = 'localhost'
212     port = int(port)
213    
214     import socket
215    
216     # AF_INET or AF_INET6 socket
217     # Get the correct address family for our host (allows IPv6 addresses)
218     for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
219                                   socket.SOCK_STREAM):
220         af, socktype, proto, canonname, sa = res
221         s = None
222         try:
223             s = socket.socket(af, socktype, proto)
224             # See http://groups.google.com/group/cherrypy-users/
225             #        browse_frm/thread/bbfe5eb39c904fe0
226             s.settimeout(1.0)
227             s.connect((host, port))
228             s.close()
229             raise IOError("Port %s is in use on %s; perhaps the previous "
230                           "server did not shut down properly." %
231                           (repr(port), repr(host)))
232         except socket.error:
233             if s:
234                 s.close()
235
236
237 def wait_for_free_port(host, port):
238     """Wait for the specified port to become free (drop requests)."""
239     if not host:
240         host = 'localhost'
241    
242     for trial in xrange(50):
243         try:
244             check_port(host, port)
245         except IOError:
246             # Give the old server thread time to free the port.
247             time.sleep(.1)
248         else:
249             return
250    
251     cherrypy.log("Port %s not free on %s" % (repr(port), repr(host)), 'HTTP')
252     raise cherrypy.NotReady("Port not free.")
253
254 def wait_for_occupied_port(host, port):
255     """Wait for the specified port to become active (receive requests)."""
256     if not host:
257         host = 'localhost'
258    
259     for trial in xrange(50):
260         try:
261             check_port(host, port)
262         except IOError:
263             return
264         else:
265             time.sleep(.1)
266    
267     cherrypy.log("Port %s not bound on %s" % (repr(port), repr(host)), 'HTTP')
268     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