1"""Manage HTTP servers with CherryPy.""" 2 3import six 4 5import cherrypy 6from cherrypy.lib.reprconf import attributes 7from cherrypy._cpcompat import text_or_bytes 8from cherrypy.process.servers import ServerAdapter 9 10 11__all__ = ('Server', ) 12 13 14class Server(ServerAdapter): 15 """An adapter for an HTTP server. 16 17 You can set attributes (like socket_host and socket_port) 18 on *this* object (which is probably cherrypy.server), and call 19 quickstart. For example:: 20 21 cherrypy.server.socket_port = 80 22 cherrypy.quickstart() 23 """ 24 25 socket_port = 8080 26 """The TCP port on which to listen for connections.""" 27 28 _socket_host = '127.0.0.1' 29 30 @property 31 def socket_host(self): # noqa: D401; irrelevant for properties 32 """The hostname or IP address on which to listen for connections. 33 34 Host values may be any IPv4 or IPv6 address, or any valid hostname. 35 The string 'localhost' is a synonym for '127.0.0.1' (or '::1', if 36 your hosts file prefers IPv6). The string '0.0.0.0' is a special 37 IPv4 entry meaning "any active interface" (INADDR_ANY), and '::' 38 is the similar IN6ADDR_ANY for IPv6. The empty string or None are 39 not allowed. 40 """ 41 return self._socket_host 42 43 @socket_host.setter 44 def socket_host(self, value): 45 if value == '': 46 raise ValueError("The empty string ('') is not an allowed value. " 47 "Use '0.0.0.0' instead to listen on all active " 48 'interfaces (INADDR_ANY).') 49 self._socket_host = value 50 51 socket_file = None 52 """If given, the name of the UNIX socket to use instead of TCP/IP. 53 54 When this option is not None, the `socket_host` and `socket_port` options 55 are ignored.""" 56 57 socket_queue_size = 5 58 """The 'backlog' argument to socket.listen(); specifies the maximum number 59 of queued connections (default 5).""" 60 61 socket_timeout = 10 62 """The timeout in seconds for accepted connections (default 10).""" 63 64 accepted_queue_size = -1 65 """The maximum number of requests which will be queued up before 66 the server refuses to accept it (default -1, meaning no limit).""" 67 68 accepted_queue_timeout = 10 69 """The timeout in seconds for attempting to add a request to the 70 queue when the queue is full (default 10).""" 71 72 shutdown_timeout = 5 73 """The time to wait for HTTP worker threads to clean up.""" 74 75 protocol_version = 'HTTP/1.1' 76 """The version string to write in the Status-Line of all HTTP responses, 77 for example, "HTTP/1.1" (the default). Depending on the HTTP server used, 78 this should also limit the supported features used in the response.""" 79 80 thread_pool = 10 81 """The number of worker threads to start up in the pool.""" 82 83 thread_pool_max = -1 84 """The maximum size of the worker-thread pool. Use -1 to indicate no limit. 85 """ 86 87 max_request_header_size = 500 * 1024 88 """The maximum number of bytes allowable in the request headers. 89 If exceeded, the HTTP server should return "413 Request Entity Too Large". 90 """ 91 92 max_request_body_size = 100 * 1024 * 1024 93 """The maximum number of bytes allowable in the request body. If exceeded, 94 the HTTP server should return "413 Request Entity Too Large".""" 95 96 instance = None 97 """If not None, this should be an HTTP server instance (such as 98 cheroot.wsgi.Server) which cherrypy.server will control. 99 Use this when you need 100 more control over object instantiation than is available in the various 101 configuration options.""" 102 103 ssl_context = None 104 """When using PyOpenSSL, an instance of SSL.Context.""" 105 106 ssl_certificate = None 107 """The filename of the SSL certificate to use.""" 108 109 ssl_certificate_chain = None 110 """When using PyOpenSSL, the certificate chain to pass to 111 Context.load_verify_locations.""" 112 113 ssl_private_key = None 114 """The filename of the private key to use with SSL.""" 115 116 ssl_ciphers = None 117 """The ciphers list of SSL.""" 118 119 if six.PY3: 120 ssl_module = 'builtin' 121 """The name of a registered SSL adaptation module to use with 122 the builtin WSGI server. Builtin options are: 'builtin' (to 123 use the SSL library built into recent versions of Python). 124 You may also register your own classes in the 125 cheroot.server.ssl_adapters dict.""" 126 else: 127 ssl_module = 'pyopenssl' 128 """The name of a registered SSL adaptation module to use with the 129 builtin WSGI server. Builtin options are 'builtin' (to use the SSL 130 library built into recent versions of Python) and 'pyopenssl' (to 131 use the PyOpenSSL project, which you must install separately). You 132 may also register your own classes in the cheroot.server.ssl_adapters 133 dict.""" 134 135 statistics = False 136 """Turns statistics-gathering on or off for aware HTTP servers.""" 137 138 nodelay = True 139 """If True (the default since 3.1), sets the TCP_NODELAY socket option.""" 140 141 wsgi_version = (1, 0) 142 """The WSGI version tuple to use with the builtin WSGI server. 143 The provided options are (1, 0) [which includes support for PEP 3333, 144 which declares it covers WSGI version 1.0.1 but still mandates the 145 wsgi.version (1, 0)] and ('u', 0), an experimental unicode version. 146 You may create and register your own experimental versions of the WSGI 147 protocol by adding custom classes to the cheroot.server.wsgi_gateways dict. 148 """ 149 150 peercreds = False 151 """If True, peer cred lookup for UNIX domain socket will put to WSGI env. 152 153 This information will then be available through WSGI env vars: 154 * X_REMOTE_PID 155 * X_REMOTE_UID 156 * X_REMOTE_GID 157 """ 158 159 peercreds_resolve = False 160 """If True, username/group will be looked up in the OS from peercreds. 161 162 This information will then be available through WSGI env vars: 163 * REMOTE_USER 164 * X_REMOTE_USER 165 * X_REMOTE_GROUP 166 """ 167 168 def __init__(self): 169 """Initialize Server instance.""" 170 self.bus = cherrypy.engine 171 self.httpserver = None 172 self.interrupt = None 173 self.running = False 174 175 def httpserver_from_self(self, httpserver=None): 176 """Return a (httpserver, bind_addr) pair based on self attributes.""" 177 if httpserver is None: 178 httpserver = self.instance 179 if httpserver is None: 180 from cherrypy import _cpwsgi_server 181 httpserver = _cpwsgi_server.CPWSGIServer(self) 182 if isinstance(httpserver, text_or_bytes): 183 # Is anyone using this? Can I add an arg? 184 httpserver = attributes(httpserver)(self) 185 return httpserver, self.bind_addr 186 187 def start(self): 188 """Start the HTTP server.""" 189 if not self.httpserver: 190 self.httpserver, self.bind_addr = self.httpserver_from_self() 191 super(Server, self).start() 192 start.priority = 75 193 194 @property 195 def bind_addr(self): 196 """Return bind address. 197 198 A (host, port) tuple for TCP sockets or a str for Unix domain sockts. 199 """ 200 if self.socket_file: 201 return self.socket_file 202 if self.socket_host is None and self.socket_port is None: 203 return None 204 return (self.socket_host, self.socket_port) 205 206 @bind_addr.setter 207 def bind_addr(self, value): 208 if value is None: 209 self.socket_file = None 210 self.socket_host = None 211 self.socket_port = None 212 elif isinstance(value, text_or_bytes): 213 self.socket_file = value 214 self.socket_host = None 215 self.socket_port = None 216 else: 217 try: 218 self.socket_host, self.socket_port = value 219 self.socket_file = None 220 except ValueError: 221 raise ValueError('bind_addr must be a (host, port) tuple ' 222 '(for TCP sockets) or a string (for Unix ' 223 'domain sockets), not %r' % value) 224 225 def base(self): 226 """Return the base for this server. 227 228 e.i. scheme://host[:port] or sock file 229 """ 230 if self.socket_file: 231 return self.socket_file 232 233 host = self.socket_host 234 if host in ('0.0.0.0', '::'): 235 # 0.0.0.0 is INADDR_ANY and :: is IN6ADDR_ANY. 236 # Look up the host name, which should be the 237 # safest thing to spit out in a URL. 238 import socket 239 host = socket.gethostname() 240 241 port = self.socket_port 242 243 if self.ssl_certificate: 244 scheme = 'https' 245 if port != 443: 246 host += ':%s' % port 247 else: 248 scheme = 'http' 249 if port != 80: 250 host += ':%s' % port 251 252 return '%s://%s' % (scheme, host) 253