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

root/tags/cherrypy-3.0.0/cherrypy/_cpserver.py

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

Changes to socket_host:

  1. wsgiserver now treats a host of "" as an alias for INADDR_ANY. The getaddrinfo call now passes host=None and sets AI_PASSIVE in this case.
  2. Server.httpserver_from_self doesn't change an empty host ("") to localhost anymore.
  3. The test suite has a new --host=<name or IP> flag.
  4. The webtest module now allows WebCase?.HOST to be "", and will connect on '127.0.0.1' if so.
  5. Lots of comments throughout to explain that the server's treatment of socket_host="" (all IPs) is different than the client's (pick any IP).
  6. Hopefully, this will fix #619 (long delay on startup).
  • 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     This is both a container and controller for "HTTP server" objects,
15     which are kept in Server.httpservers, a dictionary of the form:
16     {httpserver: bind_addr} where 'bind_addr' is usually a (host, port)
17     tuple.
18     
19     Most often, you will only be starting a single HTTP server. In this
20     common case, you can set attributes (like socket_host and socket_port)
21     on *this* object (which is probably cherrypy.server), and call
22     quickstart. For example:
23     
24         cherrypy.server.socket_port = 80
25         cherrypy.server.quickstart()
26     
27     But if you need to start more than one HTTP server (to serve on multiple
28     ports, or protocols, etc.), you can manually register each one and then
29     control them all through this object:
30     
31         s1 = MyWSGIServer(host='', port=80)
32         s2 = another.HTTPServer(host='localhost', SSL=True)
33         cherrypy.server.httpservers = {s1: ('', 80), s2: ('localhost', 443)}
34         # Note we do not use quickstart when we define our own httpservers
35         cherrypy.server.start()
36     
37     Whether you use quickstart(), or define your own httpserver entries and
38     use start(), you'll find that the start, wait, restart, and stop methods
39     work the same way, controlling all registered httpserver objects at once.
40     """
41    
42     socket_port = 8080
43     socket_host = ''
44     socket_file = ''
45     socket_queue_size = 5
46     socket_timeout = 10
47     protocol_version = 'HTTP/1.1'
48     reverse_dns = False
49     thread_pool = 10
50     max_request_header_size = 500 * 1024
51     max_request_body_size = 100 * 1024 * 1024
52     instance = None
53     ssl_certificate = None
54     ssl_private_key = None
55    
56     def __init__(self):
57         self.httpservers = {}
58         self.interrupt = None
59    
60     def quickstart(self, server=None):
61         """Start from defaults. MUST be called from the main thread.
62         
63         This function works like CherryPy 2's server.start(). It loads and
64         starts an httpserver based on the given server object (if provided)
65         and attributes of self.
66         """
67         httpserver, bind_addr = self.httpserver_from_self(server)
68         self.httpservers[httpserver] = bind_addr
69         self.start()
70    
71     def httpserver_from_self(self, httpserver=None):
72         """Return a (httpserver, bind_addr) pair based on self attributes."""
73         if httpserver is None:
74             httpserver = self.instance
75         if httpserver is None:
76             from cherrypy import _cpwsgi
77             httpserver = _cpwsgi.CPWSGIServer()
78         if isinstance(httpserver, basestring):
79             httpserver = attributes(httpserver)()
80        
81         if self.socket_file:
82             return httpserver, self.socket_file
83        
84         host = self.socket_host
85         port = self.socket_port
86         return httpserver, (host, port)
87    
88     def start(self):
89         """Start all registered HTTP servers."""
90         self.interrupt = None
91         if not self.httpservers:
92             raise ValueError("No HTTP servers have been created. "
93                              "Try server.quickstart instead.")
94         for httpserver in self.httpservers:
95             self._start_http(httpserver)
96    
97     def _start_http(self, httpserver):
98         """Start the given httpserver in a new thread."""
99         scheme = "http"
100         if getattr(httpserver, "ssl_certificate", None):
101             scheme = "https"
102         bind_addr = self.httpservers[httpserver]
103         if isinstance(bind_addr, tuple):
104             wait_for_free_port(*bind_addr)
105             host, port = bind_addr
106             if not host:
107                 host = '0.0.0.0'
108             on_what = "%s://%s:%s/" % (scheme, host, port)
109         else:
110             on_what = "socket file: %s" % bind_addr
111        
112         t = threading.Thread(target=self._start_http_thread, args=(httpserver,))
113         t.setName("CPHTTPServer " + t.getName())
114         t.start()
115        
116         self.wait(httpserver)
117         cherrypy.log("Serving %s on %s" % (scheme.upper(), on_what), 'HTTP')
118    
119     def _start_http_thread(self, httpserver):
120         """HTTP servers MUST be started in new threads, so that the
121         main thread persists to receive KeyboardInterrupt's. If an
122         exception is raised in the httpserver's thread then it's
123         trapped here, and the httpserver(s) and engine are shut down.
124         """
125         try:
126             httpserver.start()
127         except KeyboardInterrupt, exc:
128             cherrypy.log("<Ctrl-C> hit: shutting down HTTP servers", "SERVER")
129             self.interrupt = exc
130             self.stop()
131             cherrypy.engine.stop()
132         except SystemExit, exc:
133             cherrypy.log("SystemExit raised: shutting down HTTP servers", "SERVER")
134             self.interrupt = exc
135             self.stop()
136             cherrypy.engine.stop()
137             raise
138    
139     def wait(self, httpserver=None):
140         """Wait until the HTTP server is ready to receive requests.
141         
142         If no httpserver is specified, wait for all registered httpservers.
143         """
144         if httpserver is None:
145             httpservers = self.httpservers.items()
146         else:
147             httpservers = [(httpserver, self.httpservers[httpserver])]
148        
149         for httpserver, bind_addr in httpservers:
150             while not (getattr(httpserver, "ready", False) or self.interrupt):
151                 time.sleep(.1)
152             if self.interrupt:
153                 raise self.interrupt
154            
155             # Wait for port to be occupied
156             if isinstance(bind_addr, tuple):
157                 wait_for_occupied_port(*bind_addr)
158    
159     def stop(self):
160         """Stop all HTTP servers."""
161         for httpserver, bind_addr in self.httpservers.items():
162             try:
163                 httpstop = httpserver.stop
164             except AttributeError:
165                 pass
166             else:
167                 # httpstop() MUST block until the server is *truly* stopped.
168                 httpstop()
169                 if isinstance(bind_addr, tuple):
170                     wait_for_free_port(*bind_addr)
171                 cherrypy.log("HTTP Server shut down", "HTTP")
172    
173     def restart(self):
174         """Restart all HTTP servers."""
175         self.stop()
176         self.start()
177
178
179 def check_port(host, port):
180     """Raise an error if the given port is not free on the given host."""
181     if not host:
182         # The empty string signifies INADDR_ANY,
183         # which should respond on localhost.
184         host = 'localhost'
185     port = int(port)
186    
187     # AF_INET or AF_INET6 socket
188     # Get the correct address family for our host (allows IPv6 addresses)
189     for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
190                                   socket.SOCK_STREAM):
191         af, socktype, proto, canonname, sa = res
192         s = None
193         try:
194             s = socket.socket(af, socktype, proto)
195             # See http://groups.google.com/group/cherrypy-users/
196             #        browse_frm/thread/bbfe5eb39c904fe0
197             s.settimeout(1.0)
198             s.connect((host, port))
199             s.close()
200             raise IOError("Port %s is in use on %s; perhaps the previous "
201                           "httpserver did not shut down properly." %
202                           (repr(port), repr(host)))
203         except socket.error:
204             if s:
205                 s.close()
206
207
208 def wait_for_free_port(host, port):
209     """Wait for the specified port to become free (drop requests)."""
210     if not host:
211         # The empty string signifies INADDR_ANY,
212         # which should respond on localhost.
213         host = 'localhost'
214    
215     for trial in xrange(50):
216         try:
217             check_port(host, port)
218         except IOError:
219             # Give the old server thread time to free the port.
220             time.sleep(.1)
221         else:
222             return
223    
224     msg = "Port %s not free on %s" % (repr(port), repr(host))
225     cherrypy.log(msg, 'HTTP')
226     raise IOError(msg)
227
228 def wait_for_occupied_port(host, port):
229     """Wait for the specified port to become active (receive requests)."""
230     if not host:
231         # The empty string signifies INADDR_ANY,
232         # which should respond on localhost.
233         host = 'localhost'
234    
235     for trial in xrange(50):
236         try:
237             check_port(host, port)
238         except IOError:
239             return
240         else:
241             time.sleep(.1)
242    
243     msg = "Port %s not bound on %s" % (repr(port), repr(host))
244     cherrypy.log(msg, 'HTTP')
245     raise IOError(msg)
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets