sock.py 5.8 KB

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