1# -*- coding: utf-8 - 2# 3# This file is part of gunicorn released under the MIT license. 4# See the NOTICE for more information. 5 6import errno 7import os 8import socket 9import stat 10import sys 11import time 12 13from gunicorn import util 14from gunicorn.six import string_types 15 16 17class BaseSocket(object): 18 19 def __init__(self, address, conf, log, fd=None): 20 self.log = log 21 self.conf = conf 22 23 self.cfg_addr = address 24 if fd is None: 25 sock = socket.socket(self.FAMILY, socket.SOCK_STREAM) 26 bound = False 27 else: 28 sock = socket.fromfd(fd, self.FAMILY, socket.SOCK_STREAM) 29 os.close(fd) 30 bound = True 31 32 self.sock = self.set_options(sock, bound=bound) 33 34 def __str__(self): 35 return "<socket %d>" % self.sock.fileno() 36 37 def __getattr__(self, name): 38 return getattr(self.sock, name) 39 40 def set_options(self, sock, bound=False): 41 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 42 if (self.conf.reuse_port 43 and hasattr(socket, 'SO_REUSEPORT')): # pragma: no cover 44 try: 45 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) 46 except socket.error as err: 47 if err.errno not in (errno.ENOPROTOOPT, errno.EINVAL): 48 raise 49 if not bound: 50 self.bind(sock) 51 sock.setblocking(0) 52 53 # make sure that the socket can be inherited 54 if hasattr(sock, "set_inheritable"): 55 sock.set_inheritable(True) 56 57 sock.listen(self.conf.backlog) 58 return sock 59 60 def bind(self, sock): 61 sock.bind(self.cfg_addr) 62 63 def close(self): 64 if self.sock is None: 65 return 66 67 try: 68 self.sock.close() 69 except socket.error as e: 70 self.log.info("Error while closing socket %s", str(e)) 71 72 self.sock = None 73 74 75class TCPSocket(BaseSocket): 76 77 FAMILY = socket.AF_INET 78 79 def __str__(self): 80 if self.conf.is_ssl: 81 scheme = "https" 82 else: 83 scheme = "http" 84 85 addr = self.sock.getsockname() 86 return "%s://%s:%d" % (scheme, addr[0], addr[1]) 87 88 def set_options(self, sock, bound=False): 89 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 90 return super(TCPSocket, self).set_options(sock, bound=bound) 91 92 93class TCP6Socket(TCPSocket): 94 95 FAMILY = socket.AF_INET6 96 97 def __str__(self): 98 (host, port, _, _) = self.sock.getsockname() 99 return "http://[%s]:%d" % (host, port) 100 101 102class UnixSocket(BaseSocket): 103 104 FAMILY = socket.AF_UNIX 105 106 def __init__(self, addr, conf, log, fd=None): 107 if fd is None: 108 try: 109 st = os.stat(addr) 110 except OSError as e: 111 if e.args[0] != errno.ENOENT: 112 raise 113 else: 114 if stat.S_ISSOCK(st.st_mode): 115 os.remove(addr) 116 else: 117 raise ValueError("%r is not a socket" % addr) 118 super(UnixSocket, self).__init__(addr, conf, log, fd=fd) 119 120 def __str__(self): 121 return "unix:%s" % self.cfg_addr 122 123 def bind(self, sock): 124 old_umask = os.umask(self.conf.umask) 125 sock.bind(self.cfg_addr) 126 util.chown(self.cfg_addr, self.conf.uid, self.conf.gid) 127 os.umask(old_umask) 128 129 130def _sock_type(addr): 131 if isinstance(addr, tuple): 132 if util.is_ipv6(addr[0]): 133 sock_type = TCP6Socket 134 else: 135 sock_type = TCPSocket 136 elif isinstance(addr, string_types): 137 sock_type = UnixSocket 138 else: 139 raise TypeError("Unable to create socket from: %r" % addr) 140 return sock_type 141 142 143def create_sockets(conf, log, fds=None): 144 """ 145 Create a new socket for the configured addresses or file descriptors. 146 147 If a configured address is a tuple then a TCP socket is created. 148 If it is a string, a Unix socket is created. Otherwise, a TypeError is 149 raised. 150 """ 151 listeners = [] 152 153 # get it only once 154 laddr = conf.address 155 156 # check ssl config early to raise the error on startup 157 # only the certfile is needed since it can contains the keyfile 158 if conf.certfile and not os.path.exists(conf.certfile): 159 raise ValueError('certfile "%s" does not exist' % conf.certfile) 160 161 if conf.keyfile and not os.path.exists(conf.keyfile): 162 raise ValueError('keyfile "%s" does not exist' % conf.keyfile) 163 164 # sockets are already bound 165 if fds is not None: 166 for fd in fds: 167 sock = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM) 168 sock_name = sock.getsockname() 169 sock_type = _sock_type(sock_name) 170 listener = sock_type(sock_name, conf, log, fd=fd) 171 listeners.append(listener) 172 173 return listeners 174 175 # no sockets is bound, first initialization of gunicorn in this env. 176 for addr in laddr: 177 sock_type = _sock_type(addr) 178 sock = None 179 for i in range(5): 180 try: 181 sock = sock_type(addr, conf, log) 182 except socket.error as e: 183 if e.args[0] == errno.EADDRINUSE: 184 log.error("Connection in use: %s", str(addr)) 185 if e.args[0] == errno.EADDRNOTAVAIL: 186 log.error("Invalid address: %s", str(addr)) 187 if i < 5: 188 msg = "connection to {addr} failed: {error}" 189 log.debug(msg.format(addr=str(addr), error=str(e))) 190 log.error("Retrying in 1 second.") 191 time.sleep(1) 192 else: 193 break 194 195 if sock is None: 196 log.error("Can't connect to %s", str(addr)) 197 sys.exit(1) 198 199 listeners.append(sock) 200 201 return listeners 202 203 204def close_sockets(listeners, unlink=True): 205 for sock in listeners: 206 sock_name = sock.getsockname() 207 sock.close() 208 if unlink and _sock_type(sock_name) is UnixSocket: 209 os.unlink(sock_name) 210