You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					210 lines
				
				5.9 KiB
			
		
		
			
		
	
	
					210 lines
				
				5.9 KiB
			| 
								 
											8 years ago
										 
									 | 
							
								# -*- coding: utf-8 -
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# This file is part of gunicorn released under the MIT license.
							 | 
						||
| 
								 | 
							
								# See the NOTICE for more information.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import errno
							 | 
						||
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import socket
							 | 
						||
| 
								 | 
							
								import stat
							 | 
						||
| 
								 | 
							
								import sys
							 | 
						||
| 
								 | 
							
								import time
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from gunicorn import util
							 | 
						||
| 
								 | 
							
								from gunicorn.six import string_types
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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)
							 | 
						||
| 
								 | 
							
								            bound = False
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            sock = socket.fromfd(fd, self.FAMILY, socket.SOCK_STREAM)
							 | 
						||
| 
								 | 
							
								            os.close(fd)
							 | 
						||
| 
								 | 
							
								            bound = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.sock = self.set_options(sock, bound=bound)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __str__(self):
							 | 
						||
| 
								 | 
							
								        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 (self.conf.reuse_port
							 | 
						||
| 
								 | 
							
								            and hasattr(socket, 'SO_REUSEPORT')):  # pragma: no cover
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
							 | 
						||
| 
								 | 
							
								            except socket.error as err:
							 | 
						||
| 
								 | 
							
								                if err.errno not in (errno.ENOPROTOOPT, errno.EINVAL):
							 | 
						||
| 
								 | 
							
								                    raise
							 | 
						||
| 
								 | 
							
								        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, _, _) = 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 _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, fds=None):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Create a new socket for the configured addresses or file descriptors.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    If a configured address is a tuple then a TCP socket is created.
							 | 
						||
| 
								 | 
							
								    If it is a string, a Unix socket is created. Otherwise, a TypeError is
							 | 
						||
| 
								 | 
							
								    raised.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    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 fds is not None:
							 | 
						||
| 
								 | 
							
								        for fd in fds:
							 | 
						||
| 
								 | 
							
								            sock = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM)
							 | 
						||
| 
								 | 
							
								            sock_name = sock.getsockname()
							 | 
						||
| 
								 | 
							
								            sock_type = _sock_type(sock_name)
							 | 
						||
| 
								 | 
							
								            listener = sock_type(sock_name, conf, log, fd=fd)
							 | 
						||
| 
								 | 
							
								            listeners.append(listener)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return listeners
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # no sockets is bound, first initialization of gunicorn in this env.
							 | 
						||
| 
								 | 
							
								    for addr in laddr:
							 | 
						||
| 
								 | 
							
								        sock_type = _sock_type(addr)
							 | 
						||
| 
								 | 
							
								        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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def close_sockets(listeners, unlink=True):
							 | 
						||
| 
								 | 
							
								    for sock in listeners:
							 | 
						||
| 
								 | 
							
								        sock_name = sock.getsockname()
							 | 
						||
| 
								 | 
							
								        sock.close()
							 | 
						||
| 
								 | 
							
								        if unlink and _sock_type(sock_name) is UnixSocket:
							 | 
						||
| 
								 | 
							
								            os.unlink(sock_name)
							 |