def handle_ping(websocket, push_socket, client_id): result = refresh_user_client(g.user, client_id) # if the user_client key wasn't present, the client missed a heartbeat but the websocket didnt time out if result == 0: for channel in g.user["channels"]: set_user_channel_status(g.user, channel, "active") send_user_status_update(g.user, channel, push_socket, "active") websocket.send(json.dumps({ "action": "pong", "data": { "message": "PONG" } }))
def handle_ping(websocket, push_socket, client_id): result = refresh_user_client(g.user, client_id) # if the user_client key wasn't present, the client missed a heartbeat but the websocket didnt time out if result == 0: for channel in g.user["channels"]: set_user_channel_status(g.user, channel, "active") send_user_status_update(g.user, channel, push_socket, "active") websocket.send(json.dumps({"action": "pong", "data": {"message": "PONG"}}))
def handle_join_channel(channel, subscribe_socket, push_socket, client_id): add_user_to_channel(g.user, channel) set_user_channel_status(g.user, channel, "active") channel_id = zmq_channel_key(channel) send_join_channel(channel, g.user, push_socket) # subscribe to events happening on this channel subscribe_socket.setsockopt(zmq.SUBSCRIBE, channel_id) join_channel_event = { "action": "join_channel", "data": { "channel": channel, "user": { "email": g.user["email"], "gravatar": g.user["gravatar"], "name": g.user["name"], "username": g.user["email"].split("@")[0], "status": "active", }, }, } # alert channel subscribers to new user packed_join_channel = json.dumps(join_channel_event) push_socket.send(" ".join([channel_id, packed_join_channel])) # alert the user's other open clients of the change self_join_channel_event = { "action": "self_join_channel", "data": { "client_id": client_id, "channel": channel, "channel_id": channel_id, }, } packed_self_join_channel = json.dumps(self_join_channel_event) push_socket.send(" ".join([str(g.user["email"]), packed_self_join_channel]))
def eventhub_client(): websocket = request.environ.get('wsgi.websocket') if not websocket: return push_socket = zmq_context.socket(zmq.PUSH) push_socket.connect(current_app.config["PUSH_ADDRESS"]) subscribe_socket = zmq_context.socket(zmq.SUB) client_id = str(uuid.uuid4()) subscribe_socket.connect(current_app.config["SUBSCRIBE_ADDRESS"]) # add yourself to your current pool of open clients add_to_user_clients(g.user, client_id) # listen for messages that happen on channels the user is subscribed to for channel in g.user["channels"]: channel_id = zmq_channel_key(channel) subscribe_socket.setsockopt(zmq.SUBSCRIBE, channel_id) # if redis was cleared, we'll need to resend the join channel event to populate the user status of open # clients channel_status = get_user_channel_status(g.user, channel) if channel_status is None: send_join_channel(channel, g.user, push_socket) set_user_channel_status(g.user, channel, "active") send_user_status_update(g.user, channel, push_socket, "active") # subscribe to events the user triggered that could affect the user's other open clients subscribe_socket.setsockopt(zmq.SUBSCRIBE, str(g.user["email"])) poller = zmq.Poller() poller.register(subscribe_socket, zmq.POLLIN) poller.register(websocket.socket, zmq.POLLIN) try: message = None while True: events = dict(poller.poll()) # Server -> Client if subscribe_socket in events: message = subscribe_socket.recv() # the message is prepended by the channel_id (for PUB/SUB reasons) channel_id, packed = message.split(" ", 1) unpacked = json.loads(packed) action = unpacked["action"] if action in ["publish_message", "join_channel", "leave_channel", "user_active", "user_offline"]: websocket.send(packed) elif action in ["self_join_channel", "self_leave_channel"]: event_type = action.split("_")[1] handle_self_channel_event(client_id, websocket, subscribe_socket, unpacked["data"], event_type) # Client -> Server if websocket.socket.fileno() in events: socket_data = websocket.receive() if socket_data is None: break socket_data = json.loads(socket_data) action = socket_data["action"] data = socket_data["data"] if action == "switch_channel": handle_switch_channel(data["channel"]) elif action == "publish_message": handle_publish_message(data, push_socket) elif action == "preview_message": handle_preview_message(data, websocket) elif action == "join_channel": handle_join_channel(data["channel"], subscribe_socket, push_socket, client_id) elif action == "leave_channel": handle_leave_channel(data["channel"], subscribe_socket, push_socket, client_id) elif action == "reorder_channels": handle_reorder_channels(data["channels"], push_socket, client_id) elif action == "ping": handle_ping(websocket, push_socket, client_id) except geventwebsocket.WebSocketError, e: print "{0} {1}".format(e.__class__.__name__, e)
handle_preview_message(data, websocket) elif action == "join_channel": handle_join_channel(data["channel"], subscribe_socket, push_socket, client_id) elif action == "leave_channel": handle_leave_channel(data["channel"], subscribe_socket, push_socket, client_id) elif action == "reorder_channels": handle_reorder_channels(data["channels"], push_socket, client_id) elif action == "ping": handle_ping(websocket, push_socket, client_id) except geventwebsocket.WebSocketError, e: print "{0} {1}".format(e.__class__.__name__, e) remove_from_user_clients(g.user, client_id) if get_active_clients_count(g.user) == 0: for channel in g.user["channels"]: set_user_channel_status(g.user, channel, "offline") send_user_status_update(g.user, channel, push_socket, "offline") # TODO(kle): figure out how to clean up websockets left in a CLOSE_WAIT state push_socket.close() subscribe_socket.close() websocket.close() return "" def handle_reorder_channels(channels, push_socket, client_id): reorder_user_channels(g.user, channels) self_reorder_channels_event = { "action": "self_reorder_channels", "data": {
def eventhub_client(): if not request.environ.get('wsgi.websocket'): return "" websocket = request.environ['wsgi.websocket'] push_socket = zmq_context.socket(zmq.PUSH) push_socket.connect(current_app.config["PUSH_ADDRESS"]) subscribe_socket = zmq_context.socket(zmq.SUB) client_id = str(uuid.uuid4()) subscribe_socket.connect(current_app.config["SUBSCRIBE_ADDRESS"]) # add yourself to your current pool of open clients add_to_user_clients(g.user, client_id) # listen for messages that happen on channels the user is subscribed to for channel in g.user["channels"]: channel_id = zmq_channel_key(channel) subscribe_socket.setsockopt(zmq.SUBSCRIBE, channel_id) # if redis was cleared, we'll need to resend the join channel event to populate the user status of open # clients channel_status = get_user_channel_status(g.user, channel) if channel_status is None: send_join_channel(channel, g.user, push_socket) set_user_channel_status(g.user, channel, "active") send_user_status_update(g.user, channel, push_socket, "active") # subscribe to events the user triggered that could affect the user's other open clients subscribe_socket.setsockopt(zmq.SUBSCRIBE, str(g.user["email"])) poller = zmq.Poller() poller.register(subscribe_socket, zmq.POLLIN) poller.register(websocket.socket, zmq.POLLIN) try: message = None while True: events = dict(poller.poll()) # Server -> Client if subscribe_socket in events: message = subscribe_socket.recv() # the message is prepended by the channel_id (for PUB/SUB reasons) channel_id, packed = message.split(" ", 1) g.msg_unpacker.feed(packed) unpacked = g.msg_unpacker.unpack() action = unpacked["action"] if action in ["publish_message", "join_channel", "leave_channel", "user_active", "user_offline"]: websocket.send(json.dumps(unpacked)) elif action in ["self_join_channel", "self_leave_channel", "self_reorder_channels"]: event_type = action.split("_")[1] handle_self_channel_event(client_id, websocket, subscribe_socket, unpacked["data"], event_type) # Client -> Server if websocket.socket.fileno() in events: socket_data = websocket.receive() if socket_data is None: break socket_data = json.loads(socket_data) action = socket_data["action"] data = socket_data["data"] if action == "switch_channel": handle_switch_channel(data["channel"]) elif action == "publish_message": handle_publish_message(data, push_socket) elif action == "preview_message": handle_preview_message(data, websocket) elif action == "join_channel": handle_join_channel(data["channel"], subscribe_socket, push_socket, client_id) elif action == "leave_channel": handle_leave_channel(data["channel"], subscribe_socket, push_socket, client_id) elif action == "reorder_channels": handle_reorder_channels(data["channels"], push_socket, client_id) except geventwebsocket.WebSocketError, e: print "{0} {1}".format(e.__class__.__name__, e)
handle_publish_message(data, push_socket) elif action == "preview_message": handle_preview_message(data, websocket) elif action == "join_channel": handle_join_channel(data["channel"], subscribe_socket, push_socket, client_id) elif action == "leave_channel": handle_leave_channel(data["channel"], subscribe_socket, push_socket, client_id) elif action == "reorder_channels": handle_reorder_channels(data["channels"], push_socket, client_id) except geventwebsocket.WebSocketError, e: print "{0} {1}".format(e.__class__.__name__, e) remove_from_user_clients(g.user, client_id) if get_active_clients_count(g.user) == 0: for channel in g.user["channels"]: set_user_channel_status(g.user, channel, "offline") send_user_status_update(g.user, channel, push_socket, "offline") # TODO(kle): figure out how to clean up websockets left in a CLOSE_WAIT state push_socket.close() subscribe_socket.close() websocket.close() return "" def handle_reorder_channels(channels, push_socket, client_id): reorder_user_channels(g.user, channels) self_reorder_channels_event = { "action": "self_reorder_channels", "data": {