|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- # -*- coding: utf-8 -
- #
- # This file is part of gunicorn released under the MIT license.
- # See the NOTICE for more information.
-
- from __future__ import print_function
-
- import email.utils
- import fcntl
- import grp
- import io
- import os
- import pkg_resources
- import pwd
- import random
- import resource
- import socket
- import stat
- import sys
- import textwrap
- import time
- import traceback
- import inspect
- import errno
- import warnings
- import cgi
-
- from gunicorn.errors import AppImportError
- from gunicorn.six import text_type
- from gunicorn.workers import SUPPORTED_WORKERS
-
- MAXFD = 1024
- REDIRECT_TO = getattr(os, 'devnull', '/dev/null')
-
- timeout_default = object()
-
- CHUNK_SIZE = (16 * 1024)
-
- MAX_BODY = 1024 * 132
-
- # Server and Date aren't technically hop-by-hop
- # headers, but they are in the purview of the
- # origin server which the WSGI spec says we should
- # act like. So we drop them and add our own.
- #
- # In the future, concatenation server header values
- # might be better, but nothing else does it and
- # dropping them is easier.
- hop_headers = set("""
- connection keep-alive proxy-authenticate proxy-authorization
- te trailers transfer-encoding upgrade
- server date
- """.split())
-
- try:
- from setproctitle import setproctitle
- def _setproctitle(title):
- setproctitle("gunicorn: %s" % title)
- except ImportError:
- def _setproctitle(title):
- return
-
-
- try:
- from importlib import import_module
- except ImportError:
- def _resolve_name(name, package, level):
- """Return the absolute name of the module to be imported."""
- if not hasattr(package, 'rindex'):
- raise ValueError("'package' not set to a string")
- dot = len(package)
- for x in range(level, 1, -1):
- try:
- dot = package.rindex('.', 0, dot)
- except ValueError:
- msg = "attempted relative import beyond top-level package"
- raise ValueError(msg)
- return "%s.%s" % (package[:dot], name)
-
- def import_module(name, package=None):
- """Import a module.
-
- The 'package' argument is required when performing a relative import. It
- specifies the package to use as the anchor point from which to resolve the
- relative import to an absolute import.
-
- """
- if name.startswith('.'):
- if not package:
- raise TypeError("relative imports require the 'package' argument")
- level = 0
- for character in name:
- if character != '.':
- break
- level += 1
- name = _resolve_name(name[level:], package, level)
- __import__(name)
- return sys.modules[name]
-
-
- def load_class(uri, default="gunicorn.workers.sync.SyncWorker",
- section="gunicorn.workers"):
- if inspect.isclass(uri):
- return uri
- if uri.startswith("egg:"):
- # uses entry points
- entry_str = uri.split("egg:")[1]
- try:
- dist, name = entry_str.rsplit("#", 1)
- except ValueError:
- dist = entry_str
- name = default
-
- try:
- return pkg_resources.load_entry_point(dist, section, name)
- except:
- exc = traceback.format_exc()
- msg = "class uri %r invalid or not found: \n\n[%s]"
- raise RuntimeError(msg % (uri, exc))
- else:
- components = uri.split('.')
- if len(components) == 1:
- while True:
- if uri.startswith("#"):
- uri = uri[1:]
-
- if uri in SUPPORTED_WORKERS:
- components = SUPPORTED_WORKERS[uri].split(".")
- break
-
- try:
- return pkg_resources.load_entry_point("gunicorn",
- section, uri)
- except:
- exc = traceback.format_exc()
- msg = "class uri %r invalid or not found: \n\n[%s]"
- raise RuntimeError(msg % (uri, exc))
-
- klass = components.pop(-1)
-
- try:
- mod = import_module('.'.join(components))
- except:
- exc = traceback.format_exc()
- msg = "class uri %r invalid or not found: \n\n[%s]"
- raise RuntimeError(msg % (uri, exc))
- return getattr(mod, klass)
-
-
- def set_owner_process(uid, gid):
- """ set user and group of workers processes """
- if gid:
- # versions of python < 2.6.2 don't manage unsigned int for
- # groups like on osx or fedora
- gid = abs(gid) & 0x7FFFFFFF
- os.setgid(gid)
- if uid:
- os.setuid(uid)
-
-
- def chown(path, uid, gid):
- gid = abs(gid) & 0x7FFFFFFF # see note above.
- os.chown(path, uid, gid)
-
-
- if sys.platform.startswith("win"):
- def _waitfor(func, pathname, waitall=False):
- # Peform the operation
- func(pathname)
- # Now setup the wait loop
- if waitall:
- dirname = pathname
- else:
- dirname, name = os.path.split(pathname)
- dirname = dirname or '.'
- # Check for `pathname` to be removed from the filesystem.
- # The exponential backoff of the timeout amounts to a total
- # of ~1 second after which the deletion is probably an error
- # anyway.
- # Testing on a i7@4.3GHz shows that usually only 1 iteration is
- # required when contention occurs.
- timeout = 0.001
- while timeout < 1.0:
- # Note we are only testing for the existance of the file(s) in
- # the contents of the directory regardless of any security or
- # access rights. If we have made it this far, we have sufficient
- # permissions to do that much using Python's equivalent of the
- # Windows API FindFirstFile.
- # Other Windows APIs can fail or give incorrect results when
- # dealing with files that are pending deletion.
- L = os.listdir(dirname)
- if not (L if waitall else name in L):
- return
- # Increase the timeout and try again
- time.sleep(timeout)
- timeout *= 2
- warnings.warn('tests may fail, delete still pending for ' + pathname,
- RuntimeWarning, stacklevel=4)
-
- def _unlink(filename):
- _waitfor(os.unlink, filename)
- else:
- _unlink = os.unlink
-
-
- def unlink(filename):
- try:
- _unlink(filename)
- except OSError as error:
- # The filename need not exist.
- if error.errno not in (errno.ENOENT, errno.ENOTDIR):
- raise
-
-
- def is_ipv6(addr):
- try:
- socket.inet_pton(socket.AF_INET6, addr)
- except socket.error: # not a valid address
- return False
- except ValueError: # ipv6 not supported on this platform
- return False
- return True
-
-
- def parse_address(netloc, default_port=8000):
- if netloc.startswith("unix://"):
- return netloc.split("unix://")[1]
-
- if netloc.startswith("unix:"):
- return netloc.split("unix:")[1]
-
- if netloc.startswith("tcp://"):
- netloc = netloc.split("tcp://")[1]
-
- # get host
- if '[' in netloc and ']' in netloc:
- host = netloc.split(']')[0][1:].lower()
- elif ':' in netloc:
- host = netloc.split(':')[0].lower()
- elif netloc == "":
- host = "0.0.0.0"
- else:
- host = netloc.lower()
-
- #get port
- netloc = netloc.split(']')[-1]
- if ":" in netloc:
- port = netloc.split(':', 1)[1]
- if not port.isdigit():
- raise RuntimeError("%r is not a valid port number." % port)
- port = int(port)
- else:
- port = default_port
- return (host, port)
-
- def get_maxfd():
- maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
- if (maxfd == resource.RLIM_INFINITY):
- maxfd = MAXFD
- return maxfd
-
-
- def close_on_exec(fd):
- flags = fcntl.fcntl(fd, fcntl.F_GETFD)
- flags |= fcntl.FD_CLOEXEC
- fcntl.fcntl(fd, fcntl.F_SETFD, flags)
-
-
- def set_non_blocking(fd):
- flags = fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK
- fcntl.fcntl(fd, fcntl.F_SETFL, flags)
-
- def close(sock):
- try:
- sock.close()
- except socket.error:
- pass
-
- try:
- from os import closerange
- except ImportError:
- def closerange(fd_low, fd_high):
- # Iterate through and close all file descriptors.
- for fd in range(fd_low, fd_high):
- try:
- os.close(fd)
- except OSError: # ERROR, fd wasn't open to begin with (ignored)
- pass
-
-
- def write_chunk(sock, data):
- if isinstance(data, text_type):
- data = data.encode('utf-8')
- chunk_size = "%X\r\n" % len(data)
- chunk = b"".join([chunk_size.encode('utf-8'), data, b"\r\n"])
- sock.sendall(chunk)
-
-
- def write(sock, data, chunked=False):
- if chunked:
- return write_chunk(sock, data)
- sock.sendall(data)
-
-
- def write_nonblock(sock, data, chunked=False):
- timeout = sock.gettimeout()
- if timeout != 0.0:
- try:
- sock.setblocking(0)
- return write(sock, data, chunked)
- finally:
- sock.setblocking(1)
- else:
- return write(sock, data, chunked)
-
-
- def writelines(sock, lines, chunked=False):
- for line in list(lines):
- write(sock, line, chunked)
-
-
- def write_error(sock, status_int, reason, mesg):
- html = textwrap.dedent("""\
- <html>
- <head>
- <title>%(reason)s</title>
- </head>
- <body>
- <h1><p>%(reason)s</p></h1>
- %(mesg)s
- </body>
- </html>
- """) % {"reason": reason, "mesg": cgi.escape(mesg)}
-
- http = textwrap.dedent("""\
- HTTP/1.1 %s %s\r
- Connection: close\r
- Content-Type: text/html\r
- Content-Length: %d\r
- \r
- %s""") % (str(status_int), reason, len(html), html)
- write_nonblock(sock, http.encode('latin1'))
-
-
- def normalize_name(name):
- return "-".join([w.lower().capitalize() for w in name.split("-")])
-
-
- def import_app(module):
- parts = module.split(":", 1)
- if len(parts) == 1:
- module, obj = module, "application"
- else:
- module, obj = parts[0], parts[1]
-
- try:
- __import__(module)
- except ImportError:
- if module.endswith(".py") and os.path.exists(module):
- msg = "Failed to find application, did you mean '%s:%s'?"
- raise ImportError(msg % (module.rsplit(".", 1)[0], obj))
- else:
- raise
-
- mod = sys.modules[module]
-
- try:
- app = eval(obj, mod.__dict__)
- except NameError:
- raise AppImportError("Failed to find application: %r" % module)
-
- if app is None:
- raise AppImportError("Failed to find application object: %r" % obj)
-
- if not callable(app):
- raise AppImportError("Application object must be callable.")
- return app
-
-
- def getcwd():
- # get current path, try to use PWD env first
- try:
- a = os.stat(os.environ['PWD'])
- b = os.stat(os.getcwd())
- if a.st_ino == b.st_ino and a.st_dev == b.st_dev:
- cwd = os.environ['PWD']
- else:
- cwd = os.getcwd()
- except:
- cwd = os.getcwd()
- return cwd
-
-
- def http_date(timestamp=None):
- """Return the current date and time formatted for a message header."""
- if timestamp is None:
- timestamp = time.time()
- s = email.utils.formatdate(timestamp, localtime=False, usegmt=True)
- return s
-
-
- def is_hoppish(header):
- return header.lower().strip() in hop_headers
-
-
- def daemonize(enable_stdio_inheritance=False):
- """\
- Standard daemonization of a process.
- http://www.svbug.com/documentation/comp.unix.programmer-FAQ/faq_2.html#SEC16
- """
- if 'GUNICORN_FD' not in os.environ:
- if os.fork():
- os._exit(0)
- os.setsid()
-
- if os.fork():
- os._exit(0)
-
- os.umask(0o22)
-
- # In both the following any file descriptors above stdin
- # stdout and stderr are left untouched. The inheritence
- # option simply allows one to have output go to a file
- # specified by way of shell redirection when not wanting
- # to use --error-log option.
-
- if not enable_stdio_inheritance:
- # Remap all of stdin, stdout and stderr on to
- # /dev/null. The expectation is that users have
- # specified the --error-log option.
-
- closerange(0, 3)
-
- fd_null = os.open(REDIRECT_TO, os.O_RDWR)
-
- if fd_null != 0:
- os.dup2(fd_null, 0)
-
- os.dup2(fd_null, 1)
- os.dup2(fd_null, 2)
-
- else:
- fd_null = os.open(REDIRECT_TO, os.O_RDWR)
-
- # Always redirect stdin to /dev/null as we would
- # never expect to need to read interactive input.
-
- if fd_null != 0:
- os.close(0)
- os.dup2(fd_null, 0)
-
- # If stdout and stderr are still connected to
- # their original file descriptors we check to see
- # if they are associated with terminal devices.
- # When they are we map them to /dev/null so that
- # are still detached from any controlling terminal
- # properly. If not we preserve them as they are.
- #
- # If stdin and stdout were not hooked up to the
- # original file descriptors, then all bets are
- # off and all we can really do is leave them as
- # they were.
- #
- # This will allow 'gunicorn ... > output.log 2>&1'
- # to work with stdout/stderr going to the file
- # as expected.
- #
- # Note that if using --error-log option, the log
- # file specified through shell redirection will
- # only be used up until the log file specified
- # by the option takes over. As it replaces stdout
- # and stderr at the file descriptor level, then
- # anything using stdout or stderr, including having
- # cached a reference to them, will still work.
-
- def redirect(stream, fd_expect):
- try:
- fd = stream.fileno()
- if fd == fd_expect and stream.isatty():
- os.close(fd)
- os.dup2(fd_null, fd)
- except AttributeError:
- pass
-
- redirect(sys.stdout, 1)
- redirect(sys.stderr, 2)
-
-
- def seed():
- try:
- random.seed(os.urandom(64))
- except NotImplementedError:
- random.seed('%s.%s' % (time.time(), os.getpid()))
-
-
- def check_is_writeable(path):
- try:
- f = open(path, 'a')
- except IOError as e:
- raise RuntimeError("Error: '%s' isn't writable [%r]" % (path, e))
- f.close()
-
-
- def to_bytestring(value, encoding="utf8"):
- """Converts a string argument to a byte string"""
- if isinstance(value, bytes):
- return value
- if not isinstance(value, text_type):
- raise TypeError('%r is not a string' % value)
-
- return value.encode(encoding)
-
- def has_fileno(obj):
- if not hasattr(obj, "fileno"):
- return False
-
- # check BytesIO case and maybe others
- try:
- obj.fileno()
- except (AttributeError, IOError, io.UnsupportedOperation):
- return False
-
- return True
-
-
- def warn(msg):
- print("!!!", file=sys.stderr)
-
- lines = msg.splitlines()
- for i, line in enumerate(lines):
- if i == 0:
- line = "WARNING: %s" % line
- print("!!! %s" % line, file=sys.stderr)
-
- print("!!!\n", file=sys.stderr)
- sys.stderr.flush()
-
-
- def make_fail_app(msg):
-
- def app(environ, start_response):
- start_response("500 Internal Server Error", [
- ("Content-Type", "text/plain"),
- ("Content-Length", str(len(msg)))
- ])
- return [msg]
-
- return app
|