| 128 | | self.interrupt = None |
|---|
| 129 | | if not self.httpservers: |
|---|
| 130 | | raise ValueError("No HTTP servers have been created. " |
|---|
| 131 | | "Try server.quickstart instead.") |
|---|
| 132 | | for httpserver in self.httpservers: |
|---|
| 133 | | self._start_http(httpserver) |
|---|
| 134 | | cherrypy.engine.subscribe('stop', self.stop) |
|---|
| 135 | | |
|---|
| 136 | | def _start_http(self, httpserver): |
|---|
| 137 | | """Start the given httpserver in a new thread.""" |
|---|
| 138 | | scheme = "http" |
|---|
| 139 | | if getattr(httpserver, "ssl_certificate", None): |
|---|
| 140 | | scheme = "https" |
|---|
| 141 | | bind_addr = self.httpservers[httpserver] |
|---|
| 142 | | if isinstance(bind_addr, tuple): |
|---|
| 143 | | wait_for_free_port(*bind_addr) |
|---|
| 144 | | host, port = bind_addr |
|---|
| 145 | | on_what = "%s://%s:%s/" % (scheme, host, port) |
|---|
| 146 | | else: |
|---|
| 147 | | on_what = "socket file: %s" % bind_addr |
|---|
| 148 | | |
|---|
| 149 | | t = threading.Thread(target=self._start_http_thread, args=(httpserver,)) |
|---|
| 150 | | t.setName("CPHTTPServer " + t.getName()) |
|---|
| 151 | | t.start() |
|---|
| 152 | | |
|---|
| 153 | | self.wait(httpserver) |
|---|
| 154 | | cherrypy.log("Serving %s on %s" % (scheme.upper(), on_what), 'HTTP') |
|---|
| 155 | | |
|---|
| 156 | | def _start_http_thread(self, httpserver): |
|---|
| 157 | | """HTTP servers MUST be started in new threads, so that the |
|---|
| 158 | | main thread persists to receive KeyboardInterrupt's. If an |
|---|
| 159 | | exception is raised in the httpserver's thread then it's |
|---|
| 160 | | trapped here, and the engine (and therefore our httpservers) |
|---|
| 161 | | are shut down. |
|---|
| 162 | | """ |
|---|
| 163 | | try: |
|---|
| 164 | | httpserver.start() |
|---|
| 165 | | except KeyboardInterrupt, exc: |
|---|
| 166 | | cherrypy.log("<Ctrl-C> hit: shutting down HTTP servers", "SERVER") |
|---|
| 167 | | self.interrupt = exc |
|---|
| 168 | | cherrypy.engine.stop() |
|---|
| 169 | | except SystemExit, exc: |
|---|
| 170 | | cherrypy.log("SystemExit raised: shutting down HTTP servers", "SERVER") |
|---|
| 171 | | self.interrupt = exc |
|---|
| 172 | | cherrypy.engine.stop() |
|---|
| 173 | | raise |
|---|
| 174 | | except: |
|---|
| 175 | | import sys |
|---|
| 176 | | self.interrupt = sys.exc_info()[1] |
|---|
| 177 | | cherrypy.log("Error in HTTP server: shutting down", "SERVER", |
|---|
| 178 | | traceback=True) |
|---|
| 179 | | cherrypy.engine.stop() |
|---|
| 180 | | raise |
|---|
| | 121 | self.mgr.start() |
|---|
| 187 | | if httpserver is None: |
|---|
| 188 | | httpservers = self.httpservers.items() |
|---|
| 189 | | else: |
|---|
| 190 | | httpservers = [(httpserver, self.httpservers[httpserver])] |
|---|
| 191 | | |
|---|
| 192 | | for httpserver, bind_addr in httpservers: |
|---|
| 193 | | while not (getattr(httpserver, "ready", False) or self.interrupt): |
|---|
| 194 | | time.sleep(.1) |
|---|
| 195 | | if self.interrupt: |
|---|
| 196 | | raise self.interrupt |
|---|
| 197 | | |
|---|
| 198 | | # Wait for port to be occupied |
|---|
| 199 | | if isinstance(bind_addr, tuple): |
|---|
| 200 | | host, port = bind_addr |
|---|
| 201 | | wait_for_occupied_port(host, port) |
|---|
| | 128 | self.mgr.wait(httpserver) |
|---|
| 247 | | |
|---|
| 248 | | def check_port(host, port): |
|---|
| 249 | | """Raise an error if the given port is not free on the given host.""" |
|---|
| 250 | | if not host: |
|---|
| 251 | | raise ValueError("Host values of '' or None are not allowed.") |
|---|
| 252 | | host = client_host(host) |
|---|
| 253 | | port = int(port) |
|---|
| 254 | | |
|---|
| 255 | | # AF_INET or AF_INET6 socket |
|---|
| 256 | | # Get the correct address family for our host (allows IPv6 addresses) |
|---|
| 257 | | for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, |
|---|
| 258 | | socket.SOCK_STREAM): |
|---|
| 259 | | af, socktype, proto, canonname, sa = res |
|---|
| 260 | | s = None |
|---|
| 261 | | try: |
|---|
| 262 | | s = socket.socket(af, socktype, proto) |
|---|
| 263 | | # See http://groups.google.com/group/cherrypy-users/ |
|---|
| 264 | | # browse_frm/thread/bbfe5eb39c904fe0 |
|---|
| 265 | | s.settimeout(1.0) |
|---|
| 266 | | s.connect((host, port)) |
|---|
| 267 | | s.close() |
|---|
| 268 | | raise IOError("Port %s is in use on %s; perhaps the previous " |
|---|
| 269 | | "httpserver did not shut down properly." % |
|---|
| 270 | | (repr(port), repr(host))) |
|---|
| 271 | | except socket.error: |
|---|
| 272 | | if s: |
|---|
| 273 | | s.close() |
|---|
| 274 | | |
|---|
| 275 | | |
|---|
| 276 | | def wait_for_free_port(host, port): |
|---|
| 277 | | """Wait for the specified port to become free (drop requests).""" |
|---|
| 278 | | if not host: |
|---|
| 279 | | raise ValueError("Host values of '' or None are not allowed.") |
|---|
| 280 | | |
|---|
| 281 | | for trial in xrange(50): |
|---|
| 282 | | try: |
|---|
| 283 | | check_port(host, port) |
|---|
| 284 | | except IOError: |
|---|
| 285 | | # Give the old server thread time to free the port. |
|---|
| 286 | | time.sleep(.1) |
|---|
| 287 | | else: |
|---|
| 288 | | return |
|---|
| 289 | | |
|---|
| 290 | | msg = "Port %s not free on %s" % (repr(port), repr(host)) |
|---|
| 291 | | cherrypy.log(msg, 'HTTP') |
|---|
| 292 | | raise IOError(msg) |
|---|
| 293 | | |
|---|
| 294 | | def wait_for_occupied_port(host, port): |
|---|
| 295 | | """Wait for the specified port to become active (receive requests).""" |
|---|
| 296 | | if not host: |
|---|
| 297 | | raise ValueError("Host values of '' or None are not allowed.") |
|---|
| 298 | | |
|---|
| 299 | | for trial in xrange(50): |
|---|
| 300 | | try: |
|---|
| 301 | | check_port(host, port) |
|---|
| 302 | | except IOError: |
|---|
| 303 | | return |
|---|
| 304 | | else: |
|---|
| 305 | | time.sleep(.1) |
|---|
| 306 | | |
|---|
| 307 | | msg = "Port %s not bound on %s" % (repr(port), repr(host)) |
|---|
| 308 | | cherrypy.log(msg, 'HTTP') |
|---|
| 309 | | raise IOError(msg) |
|---|