Source code for bachata.tornado

"""WebSocket handler for Tornado."""
import json
import asyncio
import logging
import tornado.gen
import tornado.websocket
import tornado.ioloop


[docs]class MessagesHandler(tornado.websocket.WebSocketHandler): """Base WebSocket handler for Tornado. Default implementation works with authentication free channels, it just generates random channel identifier. Redefine :meth:`.get_channel` in subclass if you want to have channels identifiers based on authenticated user. """ @property def loop(self): io_loop = tornado.ioloop.IOLoop.current() return io_loop.asyncio_loop
[docs] def open(self): """Open WebSocket connection and add socket channel to messages center. It also performs :meth:`.authenticate` coroutine call, so :meth:`.get_channel` can rely on current authenticated user. """ self.loop.create_task(self.open_async())
@asyncio.coroutine def open_async(self): yield from self.authenticate() channel = self.get_channel() if channel: mc = self.get_messages_center() mc.add_socket(channel, self) yield from self.on_open() else: io_loop = tornado.ioloop.IOLoop.current() io_loop.add_callback(self.on_auth_error) @asyncio.coroutine
[docs] def authenticate(self): """Authenticate and load current user, asyncio coroutine. Default implementation is empty, so real authentication logic should be implemented in subclass. """ return
@asyncio.coroutine
[docs] def on_open(self): """Is called on connection success. """ pass
[docs] def on_close(self): """Is called on connection failure. """ self.get_messages_center().del_socket(self.get_channel(), self)
[docs] def on_message(self, raw_message): """Process message to messages center. """ if isinstance(raw_message, bytes): str_message = raw_message.decode('utf-8') else: str_message = raw_message self.loop.create_task( self.get_messages_center().process(str_message, self))
[docs] def on_auth_error(self): """Close connection on authorization error.""" self.close(reason="Error: not authorized for channel access")
[docs] def get_channel(self): """Get channel string identifier for connection. Method must be defined in subclass. Implementation example:: class MyMessagesHandler(talkie.tornado.MessagesHandler): def get_channel(self): if self.current_user: return 'channel:%s' % self.current_user.id """ raise NotImplementedError
[docs] def get_messages_center(self): """Get messages center for WebSocket. Method must be defined in subclass. Implementation example:: def get_messages_center(self): # bachata.RedisMessagesCenter instance return self.application.messages """ raise NotImplementedError