convenience.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import sys
  2. import warnings
  3. from eventlet import greenpool
  4. from eventlet import greenthread
  5. from eventlet import support
  6. from eventlet.green import socket
  7. from eventlet.support import greenlets as greenlet
  8. def connect(addr, family=socket.AF_INET, bind=None):
  9. """Convenience function for opening client sockets.
  10. :param addr: Address of the server to connect to. For TCP sockets, this is a (host, port) tuple.
  11. :param family: Socket family, optional. See :mod:`socket` documentation for available families.
  12. :param bind: Local address to bind to, optional.
  13. :return: The connected green socket object.
  14. """
  15. sock = socket.socket(family, socket.SOCK_STREAM)
  16. if bind is not None:
  17. sock.bind(bind)
  18. sock.connect(addr)
  19. return sock
  20. class ReuseRandomPortWarning(Warning):
  21. pass
  22. class ReusePortUnavailableWarning(Warning):
  23. pass
  24. def listen(addr, family=socket.AF_INET, backlog=50, reuse_addr=True, reuse_port=None):
  25. """Convenience function for opening server sockets. This
  26. socket can be used in :func:`~eventlet.serve` or a custom ``accept()`` loop.
  27. Sets SO_REUSEADDR on the socket to save on annoyance.
  28. :param addr: Address to listen on. For TCP sockets, this is a (host, port) tuple.
  29. :param family: Socket family, optional. See :mod:`socket` documentation for available families.
  30. :param backlog:
  31. The maximum number of queued connections. Should be at least 1; the maximum
  32. value is system-dependent.
  33. :return: The listening green socket object.
  34. """
  35. sock = socket.socket(family, socket.SOCK_STREAM)
  36. if reuse_addr and sys.platform[:3] != 'win':
  37. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  38. if family in (socket.AF_INET, socket.AF_INET6) and addr[1] == 0:
  39. if reuse_port:
  40. warnings.warn(
  41. '''listen on random port (0) with SO_REUSEPORT is dangerous.
  42. Double check your intent.
  43. Example problem: https://github.com/eventlet/eventlet/issues/411''',
  44. ReuseRandomPortWarning, stacklevel=3)
  45. elif reuse_port is None:
  46. reuse_port = True
  47. if reuse_port and hasattr(socket, 'SO_REUSEPORT'):
  48. # NOTE(zhengwei): linux kernel >= 3.9
  49. try:
  50. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
  51. # OSError is enough on Python 3+
  52. except (OSError, socket.error) as ex:
  53. if support.get_errno(ex) in (22, 92):
  54. # A famous platform defines unsupported socket option.
  55. # https://github.com/eventlet/eventlet/issues/380
  56. # https://github.com/eventlet/eventlet/issues/418
  57. warnings.warn(
  58. '''socket.SO_REUSEPORT is defined but not supported.
  59. On Windows: known bug, wontfix.
  60. On other systems: please comment in the issue linked below.
  61. More information: https://github.com/eventlet/eventlet/issues/380''',
  62. ReusePortUnavailableWarning, stacklevel=3)
  63. sock.bind(addr)
  64. sock.listen(backlog)
  65. return sock
  66. class StopServe(Exception):
  67. """Exception class used for quitting :func:`~eventlet.serve` gracefully."""
  68. pass
  69. def _stop_checker(t, server_gt, conn):
  70. try:
  71. try:
  72. t.wait()
  73. finally:
  74. conn.close()
  75. except greenlet.GreenletExit:
  76. pass
  77. except Exception:
  78. greenthread.kill(server_gt, *sys.exc_info())
  79. def serve(sock, handle, concurrency=1000):
  80. """Runs a server on the supplied socket. Calls the function *handle* in a
  81. separate greenthread for every incoming client connection. *handle* takes
  82. two arguments: the client socket object, and the client address::
  83. def myhandle(client_sock, client_addr):
  84. print("client connected", client_addr)
  85. eventlet.serve(eventlet.listen(('127.0.0.1', 9999)), myhandle)
  86. Returning from *handle* closes the client socket.
  87. :func:`serve` blocks the calling greenthread; it won't return until
  88. the server completes. If you desire an immediate return,
  89. spawn a new greenthread for :func:`serve`.
  90. Any uncaught exceptions raised in *handle* are raised as exceptions
  91. from :func:`serve`, terminating the server, so be sure to be aware of the
  92. exceptions your application can raise. The return value of *handle* is
  93. ignored.
  94. Raise a :class:`~eventlet.StopServe` exception to gracefully terminate the
  95. server -- that's the only way to get the server() function to return rather
  96. than raise.
  97. The value in *concurrency* controls the maximum number of
  98. greenthreads that will be open at any time handling requests. When
  99. the server hits the concurrency limit, it stops accepting new
  100. connections until the existing ones complete.
  101. """
  102. pool = greenpool.GreenPool(concurrency)
  103. server_gt = greenthread.getcurrent()
  104. while True:
  105. try:
  106. conn, addr = sock.accept()
  107. gt = pool.spawn(handle, conn, addr)
  108. gt.link(_stop_checker, server_gt, conn)
  109. conn, addr, gt = None, None, None
  110. except StopServe:
  111. return
  112. def wrap_ssl(sock, *a, **kw):
  113. """Convenience function for converting a regular socket into an
  114. SSL socket. Has the same interface as :func:`ssl.wrap_socket`,
  115. but can also use PyOpenSSL. Though, note that it ignores the
  116. `cert_reqs`, `ssl_version`, `ca_certs`, `do_handshake_on_connect`,
  117. and `suppress_ragged_eofs` arguments when using PyOpenSSL.
  118. The preferred idiom is to call wrap_ssl directly on the creation
  119. method, e.g., ``wrap_ssl(connect(addr))`` or
  120. ``wrap_ssl(listen(addr), server_side=True)``. This way there is
  121. no "naked" socket sitting around to accidentally corrupt the SSL
  122. session.
  123. :return Green SSL object.
  124. """
  125. return wrap_ssl_impl(sock, *a, **kw)
  126. try:
  127. from eventlet.green import ssl
  128. wrap_ssl_impl = ssl.wrap_socket
  129. except ImportError:
  130. # trying PyOpenSSL
  131. try:
  132. from eventlet.green.OpenSSL import SSL
  133. except ImportError:
  134. def wrap_ssl_impl(*a, **kw):
  135. raise ImportError(
  136. "To use SSL with Eventlet, you must install PyOpenSSL or use Python 2.7 or later.")
  137. else:
  138. def wrap_ssl_impl(sock, keyfile=None, certfile=None, server_side=False,
  139. cert_reqs=None, ssl_version=None, ca_certs=None,
  140. do_handshake_on_connect=True,
  141. suppress_ragged_eofs=True, ciphers=None):
  142. # theoretically the ssl_version could be respected in this line
  143. context = SSL.Context(SSL.SSLv23_METHOD)
  144. if certfile is not None:
  145. context.use_certificate_file(certfile)
  146. if keyfile is not None:
  147. context.use_privatekey_file(keyfile)
  148. context.set_verify(SSL.VERIFY_NONE, lambda *x: True)
  149. connection = SSL.Connection(context, sock)
  150. if server_side:
  151. connection.set_accept_state()
  152. else:
  153. connection.set_connect_state()
  154. return connection