123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- # -*- coding: utf-8 -
- #
- # This file is part of gunicorn released under the MIT license.
- # See the NOTICE for more information.
-
- import errno
- import fcntl
- import os
- import socket
- import stat
- import sys
- import time
-
- from gunicorn import util
- from gunicorn.six import string_types
-
- SD_LISTEN_FDS_START = 3
-
-
- class BaseSocket(object):
-
- def __init__(self, address, conf, log, fd=None):
- self.log = log
- self.conf = conf
-
- self.cfg_addr = address
- if fd is None:
- sock = socket.socket(self.FAMILY, socket.SOCK_STREAM)
- else:
- sock = socket.fromfd(fd, self.FAMILY, socket.SOCK_STREAM)
-
- self.sock = self.set_options(sock, bound=(fd is not None))
-
- def __str__(self, name):
- return "<socket %d>" % self.sock.fileno()
-
- def __getattr__(self, name):
- return getattr(self.sock, name)
-
- def set_options(self, sock, bound=False):
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- if not bound:
- self.bind(sock)
- sock.setblocking(0)
-
- # make sure that the socket can be inherited
- if hasattr(sock, "set_inheritable"):
- sock.set_inheritable(True)
-
- sock.listen(self.conf.backlog)
- return sock
-
- def bind(self, sock):
- sock.bind(self.cfg_addr)
-
- def close(self):
- if self.sock is None:
- return
-
- try:
- self.sock.close()
- except socket.error as e:
- self.log.info("Error while closing socket %s", str(e))
-
- self.sock = None
-
-
- class TCPSocket(BaseSocket):
-
- FAMILY = socket.AF_INET
-
- def __str__(self):
- if self.conf.is_ssl:
- scheme = "https"
- else:
- scheme = "http"
-
- addr = self.sock.getsockname()
- return "%s://%s:%d" % (scheme, addr[0], addr[1])
-
- def set_options(self, sock, bound=False):
- sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
- return super(TCPSocket, self).set_options(sock, bound=bound)
-
-
- class TCP6Socket(TCPSocket):
-
- FAMILY = socket.AF_INET6
-
- def __str__(self):
- (host, port, fl, sc) = self.sock.getsockname()
- return "http://[%s]:%d" % (host, port)
-
-
- class UnixSocket(BaseSocket):
-
- FAMILY = socket.AF_UNIX
-
- def __init__(self, addr, conf, log, fd=None):
- if fd is None:
- try:
- st = os.stat(addr)
- except OSError as e:
- if e.args[0] != errno.ENOENT:
- raise
- else:
- if stat.S_ISSOCK(st.st_mode):
- os.remove(addr)
- else:
- raise ValueError("%r is not a socket" % addr)
- super(UnixSocket, self).__init__(addr, conf, log, fd=fd)
-
- def __str__(self):
- return "unix:%s" % self.cfg_addr
-
- def bind(self, sock):
- old_umask = os.umask(self.conf.umask)
- sock.bind(self.cfg_addr)
- util.chown(self.cfg_addr, self.conf.uid, self.conf.gid)
- os.umask(old_umask)
-
- def close(self):
- os.unlink(self.cfg_addr)
- super(UnixSocket, self).close()
-
-
- def _sock_type(addr):
- if isinstance(addr, tuple):
- if util.is_ipv6(addr[0]):
- sock_type = TCP6Socket
- else:
- sock_type = TCPSocket
- elif isinstance(addr, string_types):
- sock_type = UnixSocket
- else:
- raise TypeError("Unable to create socket from: %r" % addr)
- return sock_type
-
-
- def create_sockets(conf, log):
- """
- Create a new socket for the given address. If the
- address is a tuple, a TCP socket is created. If it
- is a string, a Unix socket is created. Otherwise
- a TypeError is raised.
- """
-
- # Systemd support, use the sockets managed by systemd and passed to
- # gunicorn.
- # http://www.freedesktop.org/software/systemd/man/systemd.socket.html
- listeners = []
- if ('LISTEN_PID' in os.environ
- and int(os.environ.get('LISTEN_PID')) == os.getpid()):
- for i in range(int(os.environ.get('LISTEN_FDS', 0))):
- fd = i + SD_LISTEN_FDS_START
- try:
- sock = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM)
- sockname = sock.getsockname()
- if isinstance(sockname, str) and sockname.startswith('/'):
- listeners.append(UnixSocket(sockname, conf, log, fd=fd))
- elif len(sockname) == 2 and '.' in sockname[0]:
- listeners.append(TCPSocket("%s:%s" % sockname, conf, log,
- fd=fd))
- elif len(sockname) == 4 and ':' in sockname[0]:
- listeners.append(TCP6Socket("[%s]:%s" % sockname[:2], conf,
- log, fd=fd))
- except socket.error:
- pass
- del os.environ['LISTEN_PID'], os.environ['LISTEN_FDS']
-
- if listeners:
- log.debug('Socket activation sockets: %s',
- ",".join([str(l) for l in listeners]))
- return listeners
-
- # get it only once
- laddr = conf.address
-
- # check ssl config early to raise the error on startup
- # only the certfile is needed since it can contains the keyfile
- if conf.certfile and not os.path.exists(conf.certfile):
- raise ValueError('certfile "%s" does not exist' % conf.certfile)
-
- if conf.keyfile and not os.path.exists(conf.keyfile):
- raise ValueError('keyfile "%s" does not exist' % conf.keyfile)
-
- # sockets are already bound
- if 'GUNICORN_FD' in os.environ:
- fds = os.environ.pop('GUNICORN_FD').split(',')
- for i, fd in enumerate(fds):
- fd = int(fd)
- addr = laddr[i]
- sock_type = _sock_type(addr)
-
- try:
- listeners.append(sock_type(addr, conf, log, fd=fd))
- except socket.error as e:
- if e.args[0] == errno.ENOTCONN:
- log.error("GUNICORN_FD should refer to an open socket.")
- else:
- raise
- return listeners
-
- # no sockets is bound, first initialization of gunicorn in this env.
- for addr in laddr:
- sock_type = _sock_type(addr)
- # If we fail to create a socket from GUNICORN_FD
- # we fall through and try and open the socket
- # normally.
- sock = None
- for i in range(5):
- try:
- sock = sock_type(addr, conf, log)
- except socket.error as e:
- if e.args[0] == errno.EADDRINUSE:
- log.error("Connection in use: %s", str(addr))
- if e.args[0] == errno.EADDRNOTAVAIL:
- log.error("Invalid address: %s", str(addr))
- if i < 5:
- msg = "connection to {addr} failed: {error}"
- log.debug(msg.format(addr=str(addr), error=str(e)))
- log.error("Retrying in 1 second.")
- time.sleep(1)
- else:
- break
-
- if sock is None:
- log.error("Can't connect to %s", str(addr))
- sys.exit(1)
-
- listeners.append(sock)
-
- return listeners
|