run-dev: Add websockets support to tornado dev proxy server.

Fixes #1036.
This commit is contained in:
K.Kanakhin 2016-10-28 15:58:36 +06:00 committed by Tim Abbott
parent 09e17fbe17
commit 870de03ede
1 changed files with 63 additions and 7 deletions

View File

@ -17,6 +17,7 @@ from tornado import httputil
from tornado import gen from tornado import gen
from tornado import web from tornado import web
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from tornado.websocket import WebSocketHandler, websocket_connect
if False: from typing import Any, Callable, Generator, Optional if False: from typing import Any, Callable, Generator, Optional
@ -142,12 +143,61 @@ def fetch_request(url, callback, **kwargs):
callback(response) callback(response)
class BaseHandler(web.RequestHandler): class BaseWebsocketHandler(WebSocketHandler):
# target server ip # target server ip
target_host = '127.0.0.1' # type: str target_host = '127.0.0.1' # type: str
# target server port # target server port
target_port = None # type: int target_port = None # type: int
def __init__(self, *args, **kwargs):
# type: (*Any, **Any) -> None
super(BaseWebsocketHandler, self).__init__(*args, **kwargs)
# define client for target websocket server
self.client = None # type: Any
def get(self, *args, **kwargs):
# type: (*Any, **Any) -> Callable
# use get method from WebsocketHandler
return super(BaseWebsocketHandler, self).get(*args, **kwargs)
def open(self):
# type: () -> None
# setup connection with target websocket server
websocket_url = "ws://{host}:{port}{uri}".format(
host=self.target_host,
port=self.target_port,
uri=self.request.uri
)
request = httpclient.HTTPRequest(websocket_url)
request.headers = self._add_request_headers(['sec-websocket-extensions'])
websocket_connect(request, callback=self.open_callback,
on_message_callback=self.on_client_message)
def open_callback(self, future):
# type: (Any) -> None
# callback on connect with target websocket server
self.client = future.result()
def on_client_message(self, message):
# type: (str) -> None
if not message:
# if message empty -> target websocket server close connection
return self.close()
if self.ws_connection:
# send message to client if connection exists
self.write_message(message, False)
def on_message(self, message, binary=False):
# type: (str, bool) -> Optional[Callable]
if not self.client:
# close websocket proxy connection if no connection with target websocket server
return self.close()
self.client.write_message(message, binary)
def check_origin(self, origin):
# type: (str) -> bool
return True
def _add_request_headers(self, exclude_lower_headers_list=None): def _add_request_headers(self, exclude_lower_headers_list=None):
# type: (Optional[List[str]]) -> httputil.HTTPHeaders # type: (Optional[List[str]]) -> httputil.HTTPHeaders
exclude_lower_headers_list = exclude_lower_headers_list or [] exclude_lower_headers_list = exclude_lower_headers_list or []
@ -157,9 +207,13 @@ class BaseHandler(web.RequestHandler):
headers.add(header, v) headers.add(header, v)
return headers return headers
def get(self):
# type: () -> None class CombineHandler(BaseWebsocketHandler):
pass
def get(self, *args, **kwargs):
# type: (*Any, **Any) -> Optional[Callable]
if self.request.headers.get("Upgrade", "").lower() == 'websocket':
return super(CombineHandler, self).get(*args, **kwargs)
def head(self): def head(self):
# type: () -> None # type: () -> None
@ -207,6 +261,8 @@ class BaseHandler(web.RequestHandler):
@web.asynchronous @web.asynchronous
def prepare(self): def prepare(self):
# type: () -> None # type: () -> None
if self.request.headers.get("Upgrade", "").lower() == 'websocket':
return super(CombineHandler, self).prepare()
url = transform_url( url = transform_url(
self.request.protocol, self.request.protocol,
self.request.path, self.request.path,
@ -233,15 +289,15 @@ class BaseHandler(web.RequestHandler):
self.finish() self.finish()
class WebPackHandler(BaseHandler): class WebPackHandler(CombineHandler):
target_port = webpack_port target_port = webpack_port
class DjangoHandler(BaseHandler): class DjangoHandler(CombineHandler):
target_port = django_port target_port = django_port
class TornadoHandler(BaseHandler): class TornadoHandler(CombineHandler):
target_port = tornado_port target_port = tornado_port