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