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

root/branches/cp3-wsgi-remix/_cpserver.py

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

Changed server.start to server.quickstart, and server.start_all to server.start.

  • Property svn:eol-style set to native
Line 
1 """Manage an HTTP server with CherryPy."""
2
3 import socket
4 import threading
5 import time
6
7 import cherrypy
8 from cherrypy.lib import attributes
9
10
11 class Server(object):
12     """Manager for a set of HTTP servers."""
13    
14     def __init__(self):
15         self.httpservers = {}
16         self.interrupt = None
17    
18     def quickstart(self, server=None):
19         """Main function for quick starts. MUST be called from the main thread.
20         
21         This function works like CherryPy 2's server.start(). It loads and
22         starts an httpserver based on the given server object, if any, and
23         config entries.
24         """
25         httpserver, bind_addr = self.httpserver_from_config(server)
26         self.httpservers[httpserver] = bind_addr
27         self.start()
28    
29     def httpserver_from_config(self, httpserver=None):
30         """Return a (httpserver, bind_addr) pair based on config settings."""
31         conf = cherrypy.config.get
32         if httpserver is None:
33             httpserver = conf('server.instance', None)
34         if httpserver is None:
35             import _cpwsgi
36             httpserver = _cpwsgi.WSGIServer()
37         if isinstance(httpserver, basestring):
38             httpserver = attributes(httpserver)()
39        
40         if conf('server.socket_port'):
41             host = conf('server.socket_host')
42             port = conf('server.socket_port')
43             if not host:
44                 host = 'localhost'
45             return httpserver, (host, port)
46         else:
47             return httpserver, conf('server.socket_file')
48    
49     def start(self):
50         """Start all registered HTTP servers."""
51         self.interrupt = None
52         for httpserver in self.httpservers:
53             self._start_http(httpserver)
54    
55     def _start_http(self, httpserver):
56         """Start the given httpserver in a new thread."""
57         bind_addr = self.httpservers[httpserver]
58         if isinstance(bind_addr, tuple):
59             wait_for_free_port(*bind_addr)
60             on_what = "http://%s:%s/" % bind_addr
61         else:
62             on_what = "socket file: %s" % bind_addr
63        
64         t = threading.Thread(target=self._start_http_thread, args=(httpserver,))
65         t.setName("CPHTTPServer " + t.getName())
66         t.start()
67        
68         self.wait(httpserver)
69         cherrypy.log("Serving HTTP on %s" % on_what, 'HTTP')
70    
71     def _start_http_thread(self, httpserver):
72         """HTTP servers MUST be started in new threads, so that the
73         main thread persists to receive KeyboardInterrupt's. If an
74         exception is raised in the httpserver's thread then it's
75         trapped here, and the httpserver(s) and engine are shut down.
76         """
77         try:
78             httpserver.start()
79         except KeyboardInterrupt, exc:
80             cherrypy.log("<Ctrl-C> hit: shutting down HTTP servers", "SERVER")
81             self.interrupt = exc
82             self.stop()
83             cherrypy.engine.stop()
84         except SystemExit, exc:
85             cherrypy.log("SystemExit raised: shutting down HTTP servers", "SERVER")
86             self.interrupt = exc
87             self.stop()
88             cherrypy.engine.stop()
89             raise
90    
91     def wait(self, httpserver=None):
92         """Wait until the HTTP server is ready to receive requests.
93         
94         If no httpserver is specified, wait for all registered httpservers.
95         """
96         if httpserver is None:
97             httpservers = self.httpservers.items()
98         else:
99             httpservers = [(httpserver, self.httpservers[httpserver])]
100        
101         for httpserver, bind_addr in httpservers:
102             while not (getattr(httpserver, "ready", False) or self.interrupt):
103                 time.sleep(.1)
104             if self.interrupt:
105                 raise self.interrupt
106            
107             # Wait for port to be occupied
108             if isinstance(bind_addr, tuple):
109                 wait_for_occupied_port(*bind_addr)
110    
111     def stop(self):
112         """Stop all HTTP server(s)."""
113         for httpserver, bind_addr in self.httpservers.items():
114             try:
115                 httpstop = httpserver.stop
116             except AttributeError:
117                 pass
118             else:
119                 # httpstop() MUST block until the server is *truly* stopped.
120                 httpstop()
121                 if isinstance(bind_addr, tuple):
122                     wait_for_free_port(*bind_addr)
123                 cherrypy.log("HTTP Server shut down", "HTTP")
124    
125     def restart(self):
126         """Restart the HTTP server."""
127         self.stop()
128         self.start()
129
130
131 def check_port(host, port):
132     """Raise an error if the given port is not free on the given host."""
133     if not host:
134         host = 'localhost'
135     port = int(port)
136    
137     # AF_INET or AF_INET6 socket
138     # Get the correct address family for our host (allows IPv6 addresses)
139     for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
140                                   socket.SOCK_STREAM):
141         af, socktype, proto, canonname, sa = res
142         s = None
143         try:
144             s = socket.socket(af, socktype, proto)
145             # See http://groups.google.com/group/cherrypy-users/
146             #        browse_frm/thread/bbfe5eb39c904fe0
147             s.settimeout(1.0)
148             s.connect((host, port))
149             s.close()
150             raise IOError("Port %s is in use on %s; perhaps the previous "
151                           "httpserver did not shut down properly." %
152                           (repr(port), repr(host)))
153         except socket.error:
154             if s:
155                 s.close()
156
157
158 def wait_for_free_port(host, port):
159     """Wait for the specified port to become free (drop requests)."""
160     if not host:
161         host = 'localhost'
162    
163     for trial in xrange(50):
164         try:
165             check_port(host, port)
166         except IOError:
167             # Give the old server thread time to free the port.
168             time.sleep(.1)
169         else:
170             return
171    
172     msg = "Port %s not free on %s" % (repr(port), repr(host))
173     cherrypy.log(msg, 'HTTP')
174     raise IOError(msg)
175
176 def wait_for_occupied_port(host, port):
177     """Wait for the specified port to become active (receive requests)."""
178     if not host:
179         host = 'localhost'
180    
181     for trial in xrange(50):
182         try:
183             check_port(host, port)
184         except IOError:
185             return
186         else:
187             time.sleep(.1)
188    
189     msg = "Port %s not bound on %s" % (repr(port), repr(host))
190     cherrypy.log(msg, 'HTTP')
191     raise IOError(msg)
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets