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

185 строки
5.8KB

  1. import asyncio
  2. import sys
  3. from urllib.parse import urlsplit
  4. from .. import exceptions
  5. import tornado.web
  6. import tornado.websocket
  7. import six
  8. def get_tornado_handler(engineio_server):
  9. class Handler(tornado.websocket.WebSocketHandler): # pragma: no cover
  10. def __init__(self, *args, **kwargs):
  11. super().__init__(*args, **kwargs)
  12. if isinstance(engineio_server.cors_allowed_origins,
  13. six.string_types):
  14. if engineio_server.cors_allowed_origins == '*':
  15. self.allowed_origins = None
  16. else:
  17. self.allowed_origins = [
  18. engineio_server.cors_allowed_origins]
  19. else:
  20. self.allowed_origins = engineio_server.cors_allowed_origins
  21. self.receive_queue = asyncio.Queue()
  22. async def get(self, *args, **kwargs):
  23. if self.request.headers.get('Upgrade', '').lower() == 'websocket':
  24. ret = super().get(*args, **kwargs)
  25. if asyncio.iscoroutine(ret):
  26. await ret
  27. else:
  28. await engineio_server.handle_request(self)
  29. async def open(self, *args, **kwargs):
  30. # this is the handler for the websocket request
  31. asyncio.ensure_future(engineio_server.handle_request(self))
  32. async def post(self, *args, **kwargs):
  33. await engineio_server.handle_request(self)
  34. async def options(self, *args, **kwargs):
  35. await engineio_server.handle_request(self)
  36. async def on_message(self, message):
  37. await self.receive_queue.put(message)
  38. async def get_next_message(self):
  39. return await self.receive_queue.get()
  40. def on_close(self):
  41. self.receive_queue.put_nowait(None)
  42. def check_origin(self, origin):
  43. if self.allowed_origins is None or origin in self.allowed_origins:
  44. return True
  45. return super().check_origin(origin)
  46. def get_compression_options(self):
  47. # enable compression
  48. return {}
  49. return Handler
  50. def translate_request(handler):
  51. """This function takes the arguments passed to the request handler and
  52. uses them to generate a WSGI compatible environ dictionary.
  53. """
  54. class AwaitablePayload(object):
  55. def __init__(self, payload):
  56. self.payload = payload or b''
  57. async def read(self, length=None):
  58. if length is None:
  59. r = self.payload
  60. self.payload = b''
  61. else:
  62. r = self.payload[:length]
  63. self.payload = self.payload[length:]
  64. return r
  65. payload = handler.request.body
  66. uri_parts = urlsplit(handler.request.path)
  67. full_uri = handler.request.path
  68. if handler.request.query: # pragma: no cover
  69. full_uri += '?' + handler.request.query
  70. environ = {
  71. 'wsgi.input': AwaitablePayload(payload),
  72. 'wsgi.errors': sys.stderr,
  73. 'wsgi.version': (1, 0),
  74. 'wsgi.async': True,
  75. 'wsgi.multithread': False,
  76. 'wsgi.multiprocess': False,
  77. 'wsgi.run_once': False,
  78. 'SERVER_SOFTWARE': 'aiohttp',
  79. 'REQUEST_METHOD': handler.request.method,
  80. 'QUERY_STRING': handler.request.query or '',
  81. 'RAW_URI': full_uri,
  82. 'SERVER_PROTOCOL': 'HTTP/%s' % handler.request.version,
  83. 'REMOTE_ADDR': '127.0.0.1',
  84. 'REMOTE_PORT': '0',
  85. 'SERVER_NAME': 'aiohttp',
  86. 'SERVER_PORT': '0',
  87. 'tornado.handler': handler
  88. }
  89. for hdr_name, hdr_value in handler.request.headers.items():
  90. hdr_name = hdr_name.upper()
  91. if hdr_name == 'CONTENT-TYPE':
  92. environ['CONTENT_TYPE'] = hdr_value
  93. continue
  94. elif hdr_name == 'CONTENT-LENGTH':
  95. environ['CONTENT_LENGTH'] = hdr_value
  96. continue
  97. key = 'HTTP_%s' % hdr_name.replace('-', '_')
  98. environ[key] = hdr_value
  99. environ['wsgi.url_scheme'] = environ.get('HTTP_X_FORWARDED_PROTO', 'http')
  100. path_info = uri_parts.path
  101. environ['PATH_INFO'] = path_info
  102. environ['SCRIPT_NAME'] = ''
  103. return environ
  104. def make_response(status, headers, payload, environ):
  105. """This function generates an appropriate response object for this async
  106. mode.
  107. """
  108. tornado_handler = environ['tornado.handler']
  109. try:
  110. tornado_handler.set_status(int(status.split()[0]))
  111. except RuntimeError: # pragma: no cover
  112. # for websocket connections Tornado does not accept a response, since
  113. # it already emitted the 101 status code
  114. return
  115. for header, value in headers:
  116. tornado_handler.set_header(header, value)
  117. tornado_handler.write(payload)
  118. tornado_handler.finish()
  119. class WebSocket(object): # pragma: no cover
  120. """
  121. This wrapper class provides a tornado WebSocket interface that is
  122. somewhat compatible with eventlet's implementation.
  123. """
  124. def __init__(self, handler):
  125. self.handler = handler
  126. self.tornado_handler = None
  127. async def __call__(self, environ):
  128. self.tornado_handler = environ['tornado.handler']
  129. self.environ = environ
  130. await self.handler(self)
  131. async def close(self):
  132. self.tornado_handler.close()
  133. async def send(self, message):
  134. try:
  135. self.tornado_handler.write_message(
  136. message, binary=isinstance(message, bytes))
  137. except tornado.websocket.WebSocketClosedError:
  138. raise exceptions.EngineIOError()
  139. async def wait(self):
  140. msg = await self.tornado_handler.get_next_message()
  141. if not isinstance(msg, six.binary_type) and \
  142. not isinstance(msg, six.text_type):
  143. raise IOError()
  144. return msg
  145. _async = {
  146. 'asyncio': True,
  147. 'translate_request': translate_request,
  148. 'make_response': make_response,
  149. 'websocket': WebSocket,
  150. }