Update cherrpy to 17.4.2

This commit is contained in:
JonnyWong16 2019-11-23 18:55:19 -08:00
parent f28e741ad7
commit 4d6279a626
131 changed files with 15864 additions and 10389 deletions

View file

@ -1,4 +1,4 @@
"""
r"""
Starting in CherryPy 3.1, cherrypy.server is implemented as an
:ref:`Engine Plugin<plugins>`. It's an instance of
:class:`cherrypy._cpserver.Server`, which is a subclass of
@ -12,10 +12,14 @@ If you need to start more than one HTTP server (to serve on multiple ports, or
protocols, etc.), you can manually register each one and then start them all
with engine.start::
s1 = ServerAdapter(cherrypy.engine, MyWSGIServer(host='0.0.0.0', port=80))
s2 = ServerAdapter(cherrypy.engine,
another.HTTPServer(host='127.0.0.1',
SSL=True))
s1 = ServerAdapter(
cherrypy.engine,
MyWSGIServer(host='0.0.0.0', port=80)
)
s2 = ServerAdapter(
cherrypy.engine,
another.HTTPServer(host='127.0.0.1', SSL=True)
)
s1.subscribe()
s2.subscribe()
cherrypy.engine.start()
@ -58,10 +62,10 @@ hello.py::
import cherrypy
class HelloWorld:
\"""Sample request handler class.\"""
'''Sample request handler class.'''
@cherrypy.expose
def index(self):
return "Hello world!"
index.exposed = True
cherrypy.tree.mount(HelloWorld())
# CherryPy autoreload must be disabled for the flup server to work
@ -113,9 +117,18 @@ Please see `Lighttpd FastCGI Docs
an explanation of the possible configuration options.
"""
import os
import sys
import time
import warnings
import contextlib
import portend
class Timeouts:
occupied = 5
free = 1
class ServerAdapter(object):
@ -150,49 +163,56 @@ class ServerAdapter(object):
def start(self):
"""Start the HTTP server."""
if self.bind_addr is None:
on_what = "unknown interface (dynamic?)"
elif isinstance(self.bind_addr, tuple):
on_what = self._get_base()
else:
on_what = "socket file: %s" % self.bind_addr
if self.running:
self.bus.log("Already serving on %s" % on_what)
self.bus.log('Already serving on %s' % self.description)
return
self.interrupt = None
if not self.httpserver:
raise ValueError("No HTTP server has been created.")
raise ValueError('No HTTP server has been created.')
# Start the httpserver in a new thread.
if isinstance(self.bind_addr, tuple):
wait_for_free_port(*self.bind_addr)
if not os.environ.get('LISTEN_PID', None):
# Start the httpserver in a new thread.
if isinstance(self.bind_addr, tuple):
portend.free(*self.bind_addr, timeout=Timeouts.free)
import threading
t = threading.Thread(target=self._start_http_thread)
t.setName("HTTPServer " + t.getName())
t.setName('HTTPServer ' + t.getName())
t.start()
self.wait()
self.running = True
self.bus.log("Serving on %s" % on_what)
self.bus.log('Serving on %s' % self.description)
start.priority = 75
@property
def description(self):
"""
A description about where this server is bound.
"""
if self.bind_addr is None:
on_what = 'unknown interface (dynamic?)'
elif isinstance(self.bind_addr, tuple):
on_what = self._get_base()
else:
on_what = 'socket file: %s' % self.bind_addr
return on_what
def _get_base(self):
if not self.httpserver:
return ''
host, port = self.bind_addr
host, port = self.bound_addr
if getattr(self.httpserver, 'ssl_adapter', None):
scheme = "https"
scheme = 'https'
if port != 443:
host += ":%s" % port
host += ':%s' % port
else:
scheme = "http"
scheme = 'http'
if port != 80:
host += ":%s" % port
host += ':%s' % port
return "%s://%s" % (scheme, host)
return '%s://%s' % (scheme, host)
def _start_http_thread(self):
"""HTTP servers MUST be running in new threads, so that the
@ -204,32 +224,52 @@ class ServerAdapter(object):
try:
self.httpserver.start()
except KeyboardInterrupt:
self.bus.log("<Ctrl-C> hit: shutting down HTTP server")
self.bus.log('<Ctrl-C> hit: shutting down HTTP server')
self.interrupt = sys.exc_info()[1]
self.bus.exit()
except SystemExit:
self.bus.log("SystemExit raised: shutting down HTTP server")
self.bus.log('SystemExit raised: shutting down HTTP server')
self.interrupt = sys.exc_info()[1]
self.bus.exit()
raise
except:
except Exception:
self.interrupt = sys.exc_info()[1]
self.bus.log("Error in HTTP server: shutting down",
self.bus.log('Error in HTTP server: shutting down',
traceback=True, level=40)
self.bus.exit()
raise
def wait(self):
"""Wait until the HTTP server is ready to receive requests."""
while not getattr(self.httpserver, "ready", False):
while not getattr(self.httpserver, 'ready', False):
if self.interrupt:
raise self.interrupt
time.sleep(.1)
# Wait for port to be occupied
if isinstance(self.bind_addr, tuple):
host, port = self.bind_addr
wait_for_occupied_port(host, port)
# bypass check when LISTEN_PID is set
if os.environ.get('LISTEN_PID', None):
return
# bypass check when running via socket-activation
# (for socket-activation the port will be managed by systemd)
if not isinstance(self.bind_addr, tuple):
return
# wait for port to be occupied
with _safe_wait(*self.bound_addr):
portend.occupied(*self.bound_addr, timeout=Timeouts.occupied)
@property
def bound_addr(self):
"""
The bind address, or if it's an ephemeral port and the
socket has been bound, return the actual port bound.
"""
host, port = self.bind_addr
if port == 0 and self.httpserver.socket:
# Bound to ephemeral port. Get the actual port allocated.
port = self.httpserver.socket.getsockname()[1]
return host, port
def stop(self):
"""Stop the HTTP server."""
@ -238,11 +278,11 @@ class ServerAdapter(object):
self.httpserver.stop()
# Wait for the socket to be truly freed.
if isinstance(self.bind_addr, tuple):
wait_for_free_port(*self.bind_addr)
portend.free(*self.bound_addr, timeout=Timeouts.free)
self.running = False
self.bus.log("HTTP Server %s shut down" % self.httpserver)
self.bus.log('HTTP Server %s shut down' % self.httpserver)
else:
self.bus.log("HTTP Server %s already shut down" % self.httpserver)
self.bus.log('HTTP Server %s already shut down' % self.httpserver)
stop.priority = 25
def restart(self):
@ -359,107 +399,18 @@ class FlupSCGIServer(object):
self.scgiserver._threadPool.maxSpare = 0
def client_host(server_host):
"""Return the host on which a client can connect to the given listener."""
if server_host == '0.0.0.0':
# 0.0.0.0 is INADDR_ANY, which should answer on localhost.
return '127.0.0.1'
if server_host in ('::', '::0', '::0.0.0.0'):
# :: is IN6ADDR_ANY, which should answer on localhost.
# ::0 and ::0.0.0.0 are non-canonical but common
# ways to write IN6ADDR_ANY.
return '::1'
return server_host
def check_port(host, port, timeout=1.0):
"""Raise an error if the given port is not free on the given host."""
if not host:
raise ValueError("Host values of '' or None are not allowed.")
host = client_host(host)
port = int(port)
import socket
# AF_INET or AF_INET6 socket
# Get the correct address family for our host (allows IPv6 addresses)
@contextlib.contextmanager
def _safe_wait(host, port):
"""
On systems where a loopback interface is not available and the
server is bound to all interfaces, it's difficult to determine
whether the server is in fact occupying the port. In this case,
just issue a warning and move on. See issue #1100.
"""
try:
info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
socket.SOCK_STREAM)
except socket.gaierror:
if ':' in host:
info = [(
socket.AF_INET6, socket.SOCK_STREAM, 0, "", (host, port, 0, 0)
)]
else:
info = [(socket.AF_INET, socket.SOCK_STREAM, 0, "", (host, port))]
for res in info:
af, socktype, proto, canonname, sa = res
s = None
try:
s = socket.socket(af, socktype, proto)
# See http://groups.google.com/group/cherrypy-users/
# browse_frm/thread/bbfe5eb39c904fe0
s.settimeout(timeout)
s.connect((host, port))
s.close()
except socket.error:
if s:
s.close()
else:
raise IOError("Port %s is in use on %s; perhaps the previous "
"httpserver did not shut down properly." %
(repr(port), repr(host)))
# Feel free to increase these defaults on slow systems:
free_port_timeout = 0.1
occupied_port_timeout = 1.0
def wait_for_free_port(host, port, timeout=None):
"""Wait for the specified port to become free (drop requests)."""
if not host:
raise ValueError("Host values of '' or None are not allowed.")
if timeout is None:
timeout = free_port_timeout
for trial in range(50):
try:
# we are expecting a free port, so reduce the timeout
check_port(host, port, timeout=timeout)
except IOError:
# Give the old server thread time to free the port.
time.sleep(timeout)
else:
return
raise IOError("Port %r not free on %r" % (port, host))
def wait_for_occupied_port(host, port, timeout=None):
"""Wait for the specified port to become active (receive requests)."""
if not host:
raise ValueError("Host values of '' or None are not allowed.")
if timeout is None:
timeout = occupied_port_timeout
for trial in range(50):
try:
check_port(host, port, timeout=timeout)
except IOError:
# port is occupied
return
else:
time.sleep(timeout)
if host == client_host(host):
raise IOError("Port %r not bound on %r" % (port, host))
# On systems where a loopback interface is not available and the
# server is bound to all interfaces, it's difficult to determine
# whether the server is in fact occupying the port. In this case,
# just issue a warning and move on. See issue #1100.
msg = "Unable to verify that the server is bound on %r" % port
warnings.warn(msg)
yield
except portend.Timeout:
if host == portend.client_host(host):
raise
msg = 'Unable to verify that the server is bound on %r' % port
warnings.warn(msg)