Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

156 строки
5.3KB

  1. import importlib
  2. import sys
  3. import six
  4. import gevent
  5. import uwsgi
  6. _websocket_available = hasattr(uwsgi, 'websocket_handshake')
  7. class Thread(gevent.Greenlet): # pragma: no cover
  8. """
  9. This wrapper class provides gevent Greenlet interface that is compatible
  10. with the standard library's Thread class.
  11. """
  12. def __init__(self, target, args=[], kwargs={}):
  13. super(Thread, self).__init__(target, *args, **kwargs)
  14. def _run(self):
  15. return self.run()
  16. class uWSGIWebSocket(object): # pragma: no cover
  17. """
  18. This wrapper class provides a uWSGI WebSocket interface that is
  19. compatible with eventlet's implementation.
  20. """
  21. def __init__(self, app):
  22. self.app = app
  23. self._sock = None
  24. def __call__(self, environ, start_response):
  25. self._sock = uwsgi.connection_fd()
  26. self.environ = environ
  27. uwsgi.websocket_handshake()
  28. self._req_ctx = None
  29. if hasattr(uwsgi, 'request_context'):
  30. # uWSGI >= 2.1.x with support for api access across-greenlets
  31. self._req_ctx = uwsgi.request_context()
  32. else:
  33. # use event and queue for sending messages
  34. from gevent.event import Event
  35. from gevent.queue import Queue
  36. from gevent.select import select
  37. self._event = Event()
  38. self._send_queue = Queue()
  39. # spawn a select greenlet
  40. def select_greenlet_runner(fd, event):
  41. """Sets event when data becomes available to read on fd."""
  42. while True:
  43. event.set()
  44. try:
  45. select([fd], [], [])[0]
  46. except ValueError:
  47. break
  48. self._select_greenlet = gevent.spawn(
  49. select_greenlet_runner,
  50. self._sock,
  51. self._event)
  52. self.app(self)
  53. def close(self):
  54. """Disconnects uWSGI from the client."""
  55. uwsgi.disconnect()
  56. if self._req_ctx is None:
  57. # better kill it here in case wait() is not called again
  58. self._select_greenlet.kill()
  59. self._event.set()
  60. def _send(self, msg):
  61. """Transmits message either in binary or UTF-8 text mode,
  62. depending on its type."""
  63. if isinstance(msg, six.binary_type):
  64. method = uwsgi.websocket_send_binary
  65. else:
  66. method = uwsgi.websocket_send
  67. if self._req_ctx is not None:
  68. method(msg, request_context=self._req_ctx)
  69. else:
  70. method(msg)
  71. def _decode_received(self, msg):
  72. """Returns either bytes or str, depending on message type."""
  73. if not isinstance(msg, six.binary_type):
  74. # already decoded - do nothing
  75. return msg
  76. # only decode from utf-8 if message is not binary data
  77. type = six.byte2int(msg[0:1])
  78. if type >= 48: # no binary
  79. return msg.decode('utf-8')
  80. # binary message, don't try to decode
  81. return msg
  82. def send(self, msg):
  83. """Queues a message for sending. Real transmission is done in
  84. wait method.
  85. Sends directly if uWSGI version is new enough."""
  86. if self._req_ctx is not None:
  87. self._send(msg)
  88. else:
  89. self._send_queue.put(msg)
  90. self._event.set()
  91. def wait(self):
  92. """Waits and returns received messages.
  93. If running in compatibility mode for older uWSGI versions,
  94. it also sends messages that have been queued by send().
  95. A return value of None means that connection was closed.
  96. This must be called repeatedly. For uWSGI < 2.1.x it must
  97. be called from the main greenlet."""
  98. while True:
  99. if self._req_ctx is not None:
  100. try:
  101. msg = uwsgi.websocket_recv(request_context=self._req_ctx)
  102. except IOError: # connection closed
  103. return None
  104. return self._decode_received(msg)
  105. else:
  106. # we wake up at least every 3 seconds to let uWSGI
  107. # do its ping/ponging
  108. event_set = self._event.wait(timeout=3)
  109. if event_set:
  110. self._event.clear()
  111. # maybe there is something to send
  112. msgs = []
  113. while True:
  114. try:
  115. msgs.append(self._send_queue.get(block=False))
  116. except gevent.queue.Empty:
  117. break
  118. for msg in msgs:
  119. self._send(msg)
  120. # maybe there is something to receive, if not, at least
  121. # ensure uWSGI does its ping/ponging
  122. try:
  123. msg = uwsgi.websocket_recv_nb()
  124. except IOError: # connection closed
  125. self._select_greenlet.kill()
  126. return None
  127. if msg: # message available
  128. return self._decode_received(msg)
  129. _async = {
  130. 'threading': sys.modules[__name__],
  131. 'thread_class': 'Thread',
  132. 'queue': importlib.import_module('gevent.queue'),
  133. 'queue_class': 'JoinableQueue',
  134. 'websocket': sys.modules[__name__] if _websocket_available else None,
  135. 'websocket_class': 'uWSGIWebSocket' if _websocket_available else None,
  136. 'sleep': gevent.sleep
  137. }