Package cherrypy :: Module _cpserver
[hide private]
[frames] | no frames]

Source Code for Module cherrypy._cpserver

  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 - def base(self):
179 """Return the base (scheme://host) for this server manager.""" 180 if self.socket_file: 181 return self.socket_file 182 183 host = self.socket_host 184 if not host: 185 # The empty string signifies INADDR_ANY. Look up the host name, 186 # which should be the safest thing to spit out in a URL. 187 host = socket.gethostname() 188 189 port = self.socket_port 190 191 if self.ssl_certificate: 192 scheme = "https" 193 if port != 443: 194 host += ":%s" % port 195 else: 196 scheme = "http" 197 if port != 80: 198 host += ":%s" % port 199 200 return "%s://%s" % (scheme, host)
201 202
203 -def check_port(host, port):
204 """Raise an error if the given port is not free on the given host.""" 205 if not host: 206 # The empty string signifies INADDR_ANY, 207 # which should respond on localhost. 208 host = 'localhost' 209 port = int(port) 210 211 # AF_INET or AF_INET6 socket 212 # Get the correct address family for our host (allows IPv6 addresses) 213 for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, 214 socket.SOCK_STREAM): 215 af, socktype, proto, canonname, sa = res 216 s = None 217 try: 218 s = socket.socket(af, socktype, proto) 219 # See http://groups.google.com/group/cherrypy-users/ 220 # browse_frm/thread/bbfe5eb39c904fe0 221 s.settimeout(1.0) 222 s.connect((host, port)) 223 s.close() 224 raise IOError("Port %s is in use on %s; perhaps the previous " 225 "httpserver did not shut down properly." % 226 (repr(port), repr(host))) 227 except socket.error: 228 if s: 229 s.close()
230 231
232 -def wait_for_free_port(host, port):
233 """Wait for the specified port to become free (drop requests).""" 234 if not host: 235 # The empty string signifies INADDR_ANY, 236 # which should respond on localhost. 237 host = 'localhost' 238 239 for trial in xrange(50): 240 try: 241 check_port(host, port) 242 except IOError: 243 # Give the old server thread time to free the port. 244 time.sleep(.1) 245 else: 246 return 247 248 msg = "Port %s not free on %s" % (repr(port), repr(host)) 249 cherrypy.log(msg, 'HTTP') 250 raise IOError(msg)
251
252 -def wait_for_occupied_port(host, port):
253 """Wait for the specified port to become active (receive requests).""" 254 if not host: 255 # The empty string signifies INADDR_ANY, 256 # which should respond on localhost. 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 msg = "Port %s not bound on %s" % (repr(port), repr(host)) 268 cherrypy.log(msg, 'HTTP') 269 raise IOError(msg)
270