1# -*- coding: utf-8 -*- 2""" 3 werkzeug.serving 4 ~~~~~~~~~~~~~~~~ 5 6 There are many ways to serve a WSGI application. While you're developing 7 it you usually don't want a full blown webserver like Apache but a simple 8 standalone one. From Python 2.5 onwards there is the `wsgiref`_ server in 9 the standard library. If you're using older versions of Python you can 10 download the package from the cheeseshop. 11 12 However there are some caveats. Sourcecode won't reload itself when 13 changed and each time you kill the server using ``^C`` you get an 14 `KeyboardInterrupt` error. While the latter is easy to solve the first 15 one can be a pain in the ass in some situations. 16 17 The easiest way is creating a small ``start-myproject.py`` that runs the 18 application:: 19 20 #!/usr/bin/env python 21 # -*- coding: utf-8 -*- 22 from myproject import make_app 23 from werkzeug.serving import run_simple 24 25 app = make_app(...) 26 run_simple('localhost', 8080, app, use_reloader=True) 27 28 You can also pass it a `extra_files` keyword argument with a list of 29 additional files (like configuration files) you want to observe. 30 31 For bigger applications you should consider using `click` 32 (http://click.pocoo.org) instead of a simple start file. 33 34 35 :copyright: 2007 Pallets 36 :license: BSD-3-Clause 37""" 38import io 39import os 40import signal 41import socket 42import sys 43 44from ._compat import PY2 45from ._compat import reraise 46from ._compat import WIN 47from ._compat import wsgi_encoding_dance 48from ._internal import _log 49from .exceptions import InternalServerError 50from .urls import uri_to_iri 51from .urls import url_parse 52from .urls import url_unquote 53 54try: 55 import socketserver 56 from http.server import BaseHTTPRequestHandler 57 from http.server import HTTPServer 58except ImportError: 59 import SocketServer as socketserver 60 from BaseHTTPServer import HTTPServer 61 from BaseHTTPServer import BaseHTTPRequestHandler 62 63try: 64 import ssl 65except ImportError: 66 67 class _SslDummy(object): 68 def __getattr__(self, name): 69 raise RuntimeError("SSL support unavailable") 70 71 ssl = _SslDummy() 72 73try: 74 import termcolor 75except ImportError: 76 termcolor = None 77 78 79def _get_openssl_crypto_module(): 80 try: 81 from OpenSSL import crypto 82 except ImportError: 83 raise TypeError("Using ad-hoc certificates requires the pyOpenSSL library.") 84 else: 85 return crypto 86 87 88ThreadingMixIn = socketserver.ThreadingMixIn 89can_fork = hasattr(os, "fork") 90 91if can_fork: 92 ForkingMixIn = socketserver.ForkingMixIn 93else: 94 95 class ForkingMixIn(object): 96 pass 97 98 99try: 100 af_unix = socket.AF_UNIX 101except AttributeError: 102 af_unix = None 103 104 105LISTEN_QUEUE = 128 106can_open_by_fd = not WIN and hasattr(socket, "fromfd") 107 108# On Python 3, ConnectionError represents the same errnos as 109# socket.error from Python 2, while socket.error is an alias for the 110# more generic OSError. 111if PY2: 112 _ConnectionError = socket.error 113else: 114 _ConnectionError = ConnectionError 115 116 117class DechunkedInput(io.RawIOBase): 118 """An input stream that handles Transfer-Encoding 'chunked'""" 119 120 def __init__(self, rfile): 121 self._rfile = rfile 122 self._done = False 123 self._len = 0 124 125 def readable(self): 126 return True 127 128 def read_chunk_len(self): 129 try: 130 line = self._rfile.readline().decode("latin1") 131 _len = int(line.strip(), 16) 132 except ValueError: 133 raise IOError("Invalid chunk header") 134 if _len < 0: 135 raise IOError("Negative chunk length not allowed") 136 return _len 137 138 def readinto(self, buf): 139 read = 0 140 while not self._done and read < len(buf): 141 if self._len == 0: 142 # This is the first chunk or we fully consumed the previous 143 # one. Read the next length of the next chunk 144 self._len = self.read_chunk_len() 145 146 if self._len == 0: 147 # Found the final chunk of size 0. The stream is now exhausted, 148 # but there is still a final newline that should be consumed 149 self._done = True 150 151 if self._len > 0: 152 # There is data (left) in this chunk, so append it to the 153 # buffer. If this operation fully consumes the chunk, this will 154 # reset self._len to 0. 155 n = min(len(buf), self._len) 156 buf[read : read + n] = self._rfile.read(n) 157 self._len -= n 158 read += n 159 160 if self._len == 0: 161 # Skip the terminating newline of a chunk that has been fully 162 # consumed. This also applies to the 0-sized final chunk 163 terminator = self._rfile.readline() 164 if terminator not in (b"\n", b"\r\n", b"\r"): 165 raise IOError("Missing chunk terminating newline") 166 167 return read 168 169 170class WSGIRequestHandler(BaseHTTPRequestHandler, object): 171 172 """A request handler that implements WSGI dispatching.""" 173 174 @property 175 def server_version(self): 176 from . import __version__ 177 178 return "Werkzeug/" + __version__ 179 180 def make_environ(self): 181 request_url = url_parse(self.path) 182 183 def shutdown_server(): 184 self.server.shutdown_signal = True 185 186 url_scheme = "http" if self.server.ssl_context is None else "https" 187 if not self.client_address: 188 self.client_address = "<local>" 189 if isinstance(self.client_address, str): 190 self.client_address = (self.client_address, 0) 191 else: 192 pass 193 path_info = url_unquote(request_url.path) 194 195 environ = { 196 "wsgi.version": (1, 0), 197 "wsgi.url_scheme": url_scheme, 198 "wsgi.input": self.rfile, 199 "wsgi.errors": sys.stderr, 200 "wsgi.multithread": self.server.multithread, 201 "wsgi.multiprocess": self.server.multiprocess, 202 "wsgi.run_once": False, 203 "werkzeug.server.shutdown": shutdown_server, 204 "SERVER_SOFTWARE": self.server_version, 205 "REQUEST_METHOD": self.command, 206 "SCRIPT_NAME": "", 207 "PATH_INFO": wsgi_encoding_dance(path_info), 208 "QUERY_STRING": wsgi_encoding_dance(request_url.query), 209 # Non-standard, added by mod_wsgi, uWSGI 210 "REQUEST_URI": wsgi_encoding_dance(self.path), 211 # Non-standard, added by gunicorn 212 "RAW_URI": wsgi_encoding_dance(self.path), 213 "REMOTE_ADDR": self.address_string(), 214 "REMOTE_PORT": self.port_integer(), 215 "SERVER_NAME": self.server.server_address[0], 216 "SERVER_PORT": str(self.server.server_address[1]), 217 "SERVER_PROTOCOL": self.request_version, 218 } 219 220 for key, value in self.get_header_items(): 221 key = key.upper().replace("-", "_") 222 value = value.replace("\r\n", "") 223 if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"): 224 key = "HTTP_" + key 225 if key in environ: 226 value = "{},{}".format(environ[key], value) 227 environ[key] = value 228 229 if environ.get("HTTP_TRANSFER_ENCODING", "").strip().lower() == "chunked": 230 environ["wsgi.input_terminated"] = True 231 environ["wsgi.input"] = DechunkedInput(environ["wsgi.input"]) 232 233 if request_url.scheme and request_url.netloc: 234 environ["HTTP_HOST"] = request_url.netloc 235 236 return environ 237 238 def run_wsgi(self): 239 if self.headers.get("Expect", "").lower().strip() == "100-continue": 240 self.wfile.write(b"HTTP/1.1 100 Continue\r\n\r\n") 241 242 self.environ = environ = self.make_environ() 243 headers_set = [] 244 headers_sent = [] 245 246 def write(data): 247 assert headers_set, "write() before start_response" 248 if not headers_sent: 249 status, response_headers = headers_sent[:] = headers_set 250 try: 251 code, msg = status.split(None, 1) 252 except ValueError: 253 code, msg = status, "" 254 code = int(code) 255 self.send_response(code, msg) 256 header_keys = set() 257 for key, value in response_headers: 258 self.send_header(key, value) 259 key = key.lower() 260 header_keys.add(key) 261 if not ( 262 "content-length" in header_keys 263 or environ["REQUEST_METHOD"] == "HEAD" 264 or code < 200 265 or code in (204, 304) 266 ): 267 self.close_connection = True 268 self.send_header("Connection", "close") 269 if "server" not in header_keys: 270 self.send_header("Server", self.version_string()) 271 if "date" not in header_keys: 272 self.send_header("Date", self.date_time_string()) 273 self.end_headers() 274 275 assert isinstance(data, bytes), "applications must write bytes" 276 self.wfile.write(data) 277 self.wfile.flush() 278 279 def start_response(status, response_headers, exc_info=None): 280 if exc_info: 281 try: 282 if headers_sent: 283 reraise(*exc_info) 284 finally: 285 exc_info = None 286 elif headers_set: 287 raise AssertionError("Headers already set") 288 headers_set[:] = [status, response_headers] 289 return write 290 291 def execute(app): 292 application_iter = app(environ, start_response) 293 try: 294 for data in application_iter: 295 write(data) 296 if not headers_sent: 297 write(b"") 298 finally: 299 if hasattr(application_iter, "close"): 300 application_iter.close() 301 application_iter = None 302 303 try: 304 execute(self.server.app) 305 except (_ConnectionError, socket.timeout) as e: 306 self.connection_dropped(e, environ) 307 except Exception: 308 if self.server.passthrough_errors: 309 raise 310 from .debug.tbtools import get_current_traceback 311 312 traceback = get_current_traceback(ignore_system_exceptions=True) 313 try: 314 # if we haven't yet sent the headers but they are set 315 # we roll back to be able to set them again. 316 if not headers_sent: 317 del headers_set[:] 318 execute(InternalServerError()) 319 except Exception: 320 pass 321 self.server.log("error", "Error on request:\n%s", traceback.plaintext) 322 323 def handle(self): 324 """Handles a request ignoring dropped connections.""" 325 rv = None 326 try: 327 rv = BaseHTTPRequestHandler.handle(self) 328 except (_ConnectionError, socket.timeout) as e: 329 self.connection_dropped(e) 330 except Exception as e: 331 if self.server.ssl_context is None or not is_ssl_error(e): 332 raise 333 if self.server.shutdown_signal: 334 self.initiate_shutdown() 335 return rv 336 337 def initiate_shutdown(self): 338 """A horrible, horrible way to kill the server for Python 2.6 and 339 later. It's the best we can do. 340 """ 341 # Windows does not provide SIGKILL, go with SIGTERM then. 342 sig = getattr(signal, "SIGKILL", signal.SIGTERM) 343 # reloader active 344 if is_running_from_reloader(): 345 os.kill(os.getpid(), sig) 346 # python 2.7 347 self.server._BaseServer__shutdown_request = True 348 # python 2.6 349 self.server._BaseServer__serving = False 350 351 def connection_dropped(self, error, environ=None): 352 """Called if the connection was closed by the client. By default 353 nothing happens. 354 """ 355 356 def handle_one_request(self): 357 """Handle a single HTTP request.""" 358 self.raw_requestline = self.rfile.readline() 359 if not self.raw_requestline: 360 self.close_connection = 1 361 elif self.parse_request(): 362 return self.run_wsgi() 363 364 def send_response(self, code, message=None): 365 """Send the response header and log the response code.""" 366 self.log_request(code) 367 if message is None: 368 message = code in self.responses and self.responses[code][0] or "" 369 if self.request_version != "HTTP/0.9": 370 hdr = "%s %d %s\r\n" % (self.protocol_version, code, message) 371 self.wfile.write(hdr.encode("ascii")) 372 373 def version_string(self): 374 return BaseHTTPRequestHandler.version_string(self).strip() 375 376 def address_string(self): 377 if getattr(self, "environ", None): 378 return self.environ["REMOTE_ADDR"] 379 elif not self.client_address: 380 return "<local>" 381 elif isinstance(self.client_address, str): 382 return self.client_address 383 else: 384 return self.client_address[0] 385 386 def port_integer(self): 387 return self.client_address[1] 388 389 def log_request(self, code="-", size="-"): 390 try: 391 path = uri_to_iri(self.path) 392 msg = "%s %s %s" % (self.command, path, self.request_version) 393 except AttributeError: 394 # path isn't set if the requestline was bad 395 msg = self.requestline 396 397 code = str(code) 398 399 if termcolor: 400 color = termcolor.colored 401 402 if code[0] == "1": # 1xx - Informational 403 msg = color(msg, attrs=["bold"]) 404 elif code[0] == "2": # 2xx - Success 405 msg = color(msg, color="white") 406 elif code == "304": # 304 - Resource Not Modified 407 msg = color(msg, color="cyan") 408 elif code[0] == "3": # 3xx - Redirection 409 msg = color(msg, color="green") 410 elif code == "404": # 404 - Resource Not Found 411 msg = color(msg, color="yellow") 412 elif code[0] == "4": # 4xx - Client Error 413 msg = color(msg, color="red", attrs=["bold"]) 414 else: # 5xx, or any other response 415 msg = color(msg, color="magenta", attrs=["bold"]) 416 417 self.log("info", '"%s" %s %s', msg, code, size) 418 419 def log_error(self, *args): 420 self.log("error", *args) 421 422 def log_message(self, format, *args): 423 self.log("info", format, *args) 424 425 def log(self, type, message, *args): 426 _log( 427 type, 428 "%s - - [%s] %s\n" 429 % (self.address_string(), self.log_date_time_string(), message % args), 430 ) 431 432 def get_header_items(self): 433 """ 434 Get an iterable list of key/value pairs representing headers. 435 436 This function provides Python 2/3 compatibility as related to the 437 parsing of request headers. Python 2.7 is not compliant with 438 RFC 3875 Section 4.1.18 which requires multiple values for headers 439 to be provided or RFC 2616 which allows for folding of multi-line 440 headers. This function will return a matching list regardless 441 of Python version. It can be removed once Python 2.7 support 442 is dropped. 443 444 :return: List of tuples containing header hey/value pairs 445 """ 446 if PY2: 447 # For Python 2, process the headers manually according to 448 # W3C RFC 2616 Section 4.2. 449 items = [] 450 for header in self.headers.headers: 451 # Remove "\r\n" from the header and split on ":" to get 452 # the field name and value. 453 try: 454 key, value = header[0:-2].split(":", 1) 455 except ValueError: 456 # If header could not be slit with : but starts with white 457 # space and it follows an existing header, it's a folded 458 # header. 459 if header[0] in ("\t", " ") and items: 460 # Pop off the last header 461 key, value = items.pop() 462 # Append the current header to the value of the last 463 # header which will be placed back on the end of the 464 # list 465 value = value + header 466 # Otherwise it's just a bad header and should error 467 else: 468 # Re-raise the value error 469 raise 470 471 # Add the key and the value once stripped of leading 472 # white space. The specification allows for stripping 473 # trailing white space but the Python 3 code does not 474 # strip trailing white space. Therefore, trailing space 475 # will be left as is to match the Python 3 behavior. 476 items.append((key, value.lstrip())) 477 else: 478 items = self.headers.items() 479 480 return items 481 482 483#: backwards compatible name if someone is subclassing it 484BaseRequestHandler = WSGIRequestHandler 485 486 487def generate_adhoc_ssl_pair(cn=None): 488 from random import random 489 490 crypto = _get_openssl_crypto_module() 491 492 # pretty damn sure that this is not actually accepted by anyone 493 if cn is None: 494 cn = "*" 495 496 cert = crypto.X509() 497 cert.set_serial_number(int(random() * sys.maxsize)) 498 cert.gmtime_adj_notBefore(0) 499 cert.gmtime_adj_notAfter(60 * 60 * 24 * 365) 500 501 subject = cert.get_subject() 502 subject.CN = cn 503 subject.O = "Dummy Certificate" # noqa: E741 504 505 issuer = cert.get_issuer() 506 issuer.CN = subject.CN 507 issuer.O = subject.O # noqa: E741 508 509 pkey = crypto.PKey() 510 pkey.generate_key(crypto.TYPE_RSA, 2048) 511 cert.set_pubkey(pkey) 512 cert.sign(pkey, "sha256") 513 514 return cert, pkey 515 516 517def make_ssl_devcert(base_path, host=None, cn=None): 518 """Creates an SSL key for development. This should be used instead of 519 the ``'adhoc'`` key which generates a new cert on each server start. 520 It accepts a path for where it should store the key and cert and 521 either a host or CN. If a host is given it will use the CN 522 ``*.host/CN=host``. 523 524 For more information see :func:`run_simple`. 525 526 .. versionadded:: 0.9 527 528 :param base_path: the path to the certificate and key. The extension 529 ``.crt`` is added for the certificate, ``.key`` is 530 added for the key. 531 :param host: the name of the host. This can be used as an alternative 532 for the `cn`. 533 :param cn: the `CN` to use. 534 """ 535 from OpenSSL import crypto 536 537 if host is not None: 538 cn = "*.%s/CN=%s" % (host, host) 539 cert, pkey = generate_adhoc_ssl_pair(cn=cn) 540 541 cert_file = base_path + ".crt" 542 pkey_file = base_path + ".key" 543 544 with open(cert_file, "wb") as f: 545 f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) 546 with open(pkey_file, "wb") as f: 547 f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey)) 548 549 return cert_file, pkey_file 550 551 552def generate_adhoc_ssl_context(): 553 """Generates an adhoc SSL context for the development server.""" 554 crypto = _get_openssl_crypto_module() 555 import tempfile 556 import atexit 557 558 cert, pkey = generate_adhoc_ssl_pair() 559 cert_handle, cert_file = tempfile.mkstemp() 560 pkey_handle, pkey_file = tempfile.mkstemp() 561 atexit.register(os.remove, pkey_file) 562 atexit.register(os.remove, cert_file) 563 564 os.write(cert_handle, crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) 565 os.write(pkey_handle, crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey)) 566 os.close(cert_handle) 567 os.close(pkey_handle) 568 ctx = load_ssl_context(cert_file, pkey_file) 569 return ctx 570 571 572def load_ssl_context(cert_file, pkey_file=None, protocol=None): 573 """Loads SSL context from cert/private key files and optional protocol. 574 Many parameters are directly taken from the API of 575 :py:class:`ssl.SSLContext`. 576 577 :param cert_file: Path of the certificate to use. 578 :param pkey_file: Path of the private key to use. If not given, the key 579 will be obtained from the certificate file. 580 :param protocol: One of the ``PROTOCOL_*`` constants in the stdlib ``ssl`` 581 module. Defaults to ``PROTOCOL_SSLv23``. 582 """ 583 if protocol is None: 584 protocol = ssl.PROTOCOL_SSLv23 585 ctx = _SSLContext(protocol) 586 ctx.load_cert_chain(cert_file, pkey_file) 587 return ctx 588 589 590class _SSLContext(object): 591 592 """A dummy class with a small subset of Python3's ``ssl.SSLContext``, only 593 intended to be used with and by Werkzeug.""" 594 595 def __init__(self, protocol): 596 self._protocol = protocol 597 self._certfile = None 598 self._keyfile = None 599 self._password = None 600 601 def load_cert_chain(self, certfile, keyfile=None, password=None): 602 self._certfile = certfile 603 self._keyfile = keyfile or certfile 604 self._password = password 605 606 def wrap_socket(self, sock, **kwargs): 607 return ssl.wrap_socket( 608 sock, 609 keyfile=self._keyfile, 610 certfile=self._certfile, 611 ssl_version=self._protocol, 612 **kwargs 613 ) 614 615 616def is_ssl_error(error=None): 617 """Checks if the given error (or the current one) is an SSL error.""" 618 exc_types = (ssl.SSLError,) 619 try: 620 from OpenSSL.SSL import Error 621 622 exc_types += (Error,) 623 except ImportError: 624 pass 625 626 if error is None: 627 error = sys.exc_info()[1] 628 return isinstance(error, exc_types) 629 630 631def select_address_family(host, port): 632 """Return ``AF_INET4``, ``AF_INET6``, or ``AF_UNIX`` depending on 633 the host and port.""" 634 # disabled due to problems with current ipv6 implementations 635 # and various operating systems. Probably this code also is 636 # not supposed to work, but I can't come up with any other 637 # ways to implement this. 638 # try: 639 # info = socket.getaddrinfo(host, port, socket.AF_UNSPEC, 640 # socket.SOCK_STREAM, 0, 641 # socket.AI_PASSIVE) 642 # if info: 643 # return info[0][0] 644 # except socket.gaierror: 645 # pass 646 if host.startswith("unix://"): 647 return socket.AF_UNIX 648 elif ":" in host and hasattr(socket, "AF_INET6"): 649 return socket.AF_INET6 650 return socket.AF_INET 651 652 653def get_sockaddr(host, port, family): 654 """Return a fully qualified socket address that can be passed to 655 :func:`socket.bind`.""" 656 if family == af_unix: 657 return host.split("://", 1)[1] 658 try: 659 res = socket.getaddrinfo( 660 host, port, family, socket.SOCK_STREAM, socket.IPPROTO_TCP 661 ) 662 except socket.gaierror: 663 return host, port 664 return res[0][4] 665 666 667class BaseWSGIServer(HTTPServer, object): 668 669 """Simple single-threaded, single-process WSGI server.""" 670 671 multithread = False 672 multiprocess = False 673 request_queue_size = LISTEN_QUEUE 674 675 def __init__( 676 self, 677 host, 678 port, 679 app, 680 handler=None, 681 passthrough_errors=False, 682 ssl_context=None, 683 fd=None, 684 ): 685 if handler is None: 686 handler = WSGIRequestHandler 687 688 self.address_family = select_address_family(host, port) 689 690 if fd is not None: 691 real_sock = socket.fromfd(fd, self.address_family, socket.SOCK_STREAM) 692 port = 0 693 694 server_address = get_sockaddr(host, int(port), self.address_family) 695 696 # remove socket file if it already exists 697 if self.address_family == af_unix and os.path.exists(server_address): 698 os.unlink(server_address) 699 HTTPServer.__init__(self, server_address, handler) 700 701 self.app = app 702 self.passthrough_errors = passthrough_errors 703 self.shutdown_signal = False 704 self.host = host 705 self.port = self.socket.getsockname()[1] 706 707 # Patch in the original socket. 708 if fd is not None: 709 self.socket.close() 710 self.socket = real_sock 711 self.server_address = self.socket.getsockname() 712 713 if ssl_context is not None: 714 if isinstance(ssl_context, tuple): 715 ssl_context = load_ssl_context(*ssl_context) 716 if ssl_context == "adhoc": 717 ssl_context = generate_adhoc_ssl_context() 718 # If we are on Python 2 the return value from socket.fromfd 719 # is an internal socket object but what we need for ssl wrap 720 # is the wrapper around it :( 721 sock = self.socket 722 if PY2 and not isinstance(sock, socket.socket): 723 sock = socket.socket(sock.family, sock.type, sock.proto, sock) 724 self.socket = ssl_context.wrap_socket(sock, server_side=True) 725 self.ssl_context = ssl_context 726 else: 727 self.ssl_context = None 728 729 def log(self, type, message, *args): 730 _log(type, message, *args) 731 732 def serve_forever(self): 733 self.shutdown_signal = False 734 try: 735 HTTPServer.serve_forever(self) 736 except KeyboardInterrupt: 737 pass 738 finally: 739 self.server_close() 740 741 def handle_error(self, request, client_address): 742 if self.passthrough_errors: 743 raise 744 # Python 2 still causes a socket.error after the earlier 745 # handling, so silence it here. 746 if isinstance(sys.exc_info()[1], _ConnectionError): 747 return 748 return HTTPServer.handle_error(self, request, client_address) 749 750 def get_request(self): 751 con, info = self.socket.accept() 752 return con, info 753 754 755class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer): 756 757 """A WSGI server that does threading.""" 758 759 multithread = True 760 daemon_threads = True 761 762 763class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer): 764 765 """A WSGI server that does forking.""" 766 767 multiprocess = True 768 769 def __init__( 770 self, 771 host, 772 port, 773 app, 774 processes=40, 775 handler=None, 776 passthrough_errors=False, 777 ssl_context=None, 778 fd=None, 779 ): 780 if not can_fork: 781 raise ValueError("Your platform does not support forking.") 782 BaseWSGIServer.__init__( 783 self, host, port, app, handler, passthrough_errors, ssl_context, fd 784 ) 785 self.max_children = processes 786 787 788def make_server( 789 host=None, 790 port=None, 791 app=None, 792 threaded=False, 793 processes=1, 794 request_handler=None, 795 passthrough_errors=False, 796 ssl_context=None, 797 fd=None, 798): 799 """Create a new server instance that is either threaded, or forks 800 or just processes one request after another. 801 """ 802 if threaded and processes > 1: 803 raise ValueError("cannot have a multithreaded and multi process server.") 804 elif threaded: 805 return ThreadedWSGIServer( 806 host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd 807 ) 808 elif processes > 1: 809 return ForkingWSGIServer( 810 host, 811 port, 812 app, 813 processes, 814 request_handler, 815 passthrough_errors, 816 ssl_context, 817 fd=fd, 818 ) 819 else: 820 return BaseWSGIServer( 821 host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd 822 ) 823 824 825def is_running_from_reloader(): 826 """Checks if the application is running from within the Werkzeug 827 reloader subprocess. 828 829 .. versionadded:: 0.10 830 """ 831 return os.environ.get("WERKZEUG_RUN_MAIN") == "true" 832 833 834def run_simple( 835 hostname, 836 port, 837 application, 838 use_reloader=False, 839 use_debugger=False, 840 use_evalex=True, 841 extra_files=None, 842 reloader_interval=1, 843 reloader_type="auto", 844 threaded=False, 845 processes=1, 846 request_handler=None, 847 static_files=None, 848 passthrough_errors=False, 849 ssl_context=None, 850): 851 """Start a WSGI application. Optional features include a reloader, 852 multithreading and fork support. 853 854 This function has a command-line interface too:: 855 856 python -m werkzeug.serving --help 857 858 .. versionadded:: 0.5 859 `static_files` was added to simplify serving of static files as well 860 as `passthrough_errors`. 861 862 .. versionadded:: 0.6 863 support for SSL was added. 864 865 .. versionadded:: 0.8 866 Added support for automatically loading a SSL context from certificate 867 file and private key. 868 869 .. versionadded:: 0.9 870 Added command-line interface. 871 872 .. versionadded:: 0.10 873 Improved the reloader and added support for changing the backend 874 through the `reloader_type` parameter. See :ref:`reloader` 875 for more information. 876 877 .. versionchanged:: 0.15 878 Bind to a Unix socket by passing a path that starts with 879 ``unix://`` as the ``hostname``. 880 881 :param hostname: The host to bind to, for example ``'localhost'``. 882 If the value is a path that starts with ``unix://`` it will bind 883 to a Unix socket instead of a TCP socket.. 884 :param port: The port for the server. eg: ``8080`` 885 :param application: the WSGI application to execute 886 :param use_reloader: should the server automatically restart the python 887 process if modules were changed? 888 :param use_debugger: should the werkzeug debugging system be used? 889 :param use_evalex: should the exception evaluation feature be enabled? 890 :param extra_files: a list of files the reloader should watch 891 additionally to the modules. For example configuration 892 files. 893 :param reloader_interval: the interval for the reloader in seconds. 894 :param reloader_type: the type of reloader to use. The default is 895 auto detection. Valid values are ``'stat'`` and 896 ``'watchdog'``. See :ref:`reloader` for more 897 information. 898 :param threaded: should the process handle each request in a separate 899 thread? 900 :param processes: if greater than 1 then handle each request in a new process 901 up to this maximum number of concurrent processes. 902 :param request_handler: optional parameter that can be used to replace 903 the default one. You can use this to replace it 904 with a different 905 :class:`~BaseHTTPServer.BaseHTTPRequestHandler` 906 subclass. 907 :param static_files: a list or dict of paths for static files. This works 908 exactly like :class:`SharedDataMiddleware`, it's actually 909 just wrapping the application in that middleware before 910 serving. 911 :param passthrough_errors: set this to `True` to disable the error catching. 912 This means that the server will die on errors but 913 it can be useful to hook debuggers in (pdb etc.) 914 :param ssl_context: an SSL context for the connection. Either an 915 :class:`ssl.SSLContext`, a tuple in the form 916 ``(cert_file, pkey_file)``, the string ``'adhoc'`` if 917 the server should automatically create one, or ``None`` 918 to disable SSL (which is the default). 919 """ 920 if not isinstance(port, int): 921 raise TypeError("port must be an integer") 922 if use_debugger: 923 from .debug import DebuggedApplication 924 925 application = DebuggedApplication(application, use_evalex) 926 if static_files: 927 from .middleware.shared_data import SharedDataMiddleware 928 929 application = SharedDataMiddleware(application, static_files) 930 931 def log_startup(sock): 932 display_hostname = hostname if hostname not in ("", "*") else "localhost" 933 quit_msg = "(Press CTRL+C to quit)" 934 if sock.family == af_unix: 935 _log("info", " * Running on %s %s", display_hostname, quit_msg) 936 else: 937 if ":" in display_hostname: 938 display_hostname = "[%s]" % display_hostname 939 port = sock.getsockname()[1] 940 _log( 941 "info", 942 " * Running on %s://%s:%d/ %s", 943 "http" if ssl_context is None else "https", 944 display_hostname, 945 port, 946 quit_msg, 947 ) 948 949 def inner(): 950 try: 951 fd = int(os.environ["WERKZEUG_SERVER_FD"]) 952 except (LookupError, ValueError): 953 fd = None 954 srv = make_server( 955 hostname, 956 port, 957 application, 958 threaded, 959 processes, 960 request_handler, 961 passthrough_errors, 962 ssl_context, 963 fd=fd, 964 ) 965 if fd is None: 966 log_startup(srv.socket) 967 srv.serve_forever() 968 969 if use_reloader: 970 # If we're not running already in the subprocess that is the 971 # reloader we want to open up a socket early to make sure the 972 # port is actually available. 973 if not is_running_from_reloader(): 974 if port == 0 and not can_open_by_fd: 975 raise ValueError( 976 "Cannot bind to a random port with enabled " 977 "reloader if the Python interpreter does " 978 "not support socket opening by fd." 979 ) 980 981 # Create and destroy a socket so that any exceptions are 982 # raised before we spawn a separate Python interpreter and 983 # lose this ability. 984 address_family = select_address_family(hostname, port) 985 server_address = get_sockaddr(hostname, port, address_family) 986 s = socket.socket(address_family, socket.SOCK_STREAM) 987 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 988 s.bind(server_address) 989 if hasattr(s, "set_inheritable"): 990 s.set_inheritable(True) 991 992 # If we can open the socket by file descriptor, then we can just 993 # reuse this one and our socket will survive the restarts. 994 if can_open_by_fd: 995 os.environ["WERKZEUG_SERVER_FD"] = str(s.fileno()) 996 s.listen(LISTEN_QUEUE) 997 log_startup(s) 998 else: 999 s.close() 1000 if address_family == af_unix: 1001 _log("info", "Unlinking %s" % server_address) 1002 os.unlink(server_address) 1003 1004 # Do not use relative imports, otherwise "python -m werkzeug.serving" 1005 # breaks. 1006 from ._reloader import run_with_reloader 1007 1008 run_with_reloader(inner, extra_files, reloader_interval, reloader_type) 1009 else: 1010 inner() 1011 1012 1013def run_with_reloader(*args, **kwargs): 1014 # People keep using undocumented APIs. Do not use this function 1015 # please, we do not guarantee that it continues working. 1016 from ._reloader import run_with_reloader 1017 1018 return run_with_reloader(*args, **kwargs) 1019 1020 1021def main(): 1022 """A simple command-line interface for :py:func:`run_simple`.""" 1023 1024 # in contrast to argparse, this works at least under Python < 2.7 1025 import optparse 1026 from .utils import import_string 1027 1028 parser = optparse.OptionParser(usage="Usage: %prog [options] app_module:app_object") 1029 parser.add_option( 1030 "-b", 1031 "--bind", 1032 dest="address", 1033 help="The hostname:port the app should listen on.", 1034 ) 1035 parser.add_option( 1036 "-d", 1037 "--debug", 1038 dest="use_debugger", 1039 action="store_true", 1040 default=False, 1041 help="Use Werkzeug's debugger.", 1042 ) 1043 parser.add_option( 1044 "-r", 1045 "--reload", 1046 dest="use_reloader", 1047 action="store_true", 1048 default=False, 1049 help="Reload Python process if modules change.", 1050 ) 1051 options, args = parser.parse_args() 1052 1053 hostname, port = None, None 1054 if options.address: 1055 address = options.address.split(":") 1056 hostname = address[0] 1057 if len(address) > 1: 1058 port = address[1] 1059 1060 if len(args) != 1: 1061 sys.stdout.write("No application supplied, or too much. See --help\n") 1062 sys.exit(1) 1063 app = import_string(args[0]) 1064 1065 run_simple( 1066 hostname=(hostname or "127.0.0.1"), 1067 port=int(port or 5000), 1068 application=app, 1069 use_reloader=options.use_reloader, 1070 use_debugger=options.use_debugger, 1071 ) 1072 1073 1074if __name__ == "__main__": 1075 main() 1076