def gc_conns(): server_state = get_state() with server_state.lock: start_time = datetime.utcnow() threshold = start_time - timedelta(seconds=15) collected_conns = [] # collect every ref in chanels # remove connections from channels for channel in server_state.channels.values(): for username, conns in list(channel.connections.items()): for conn in conns: if conn.last_active < threshold: channel.connections[username].remove(conn) collected_conns.append(conn) channel.after_parted(username) # remove old conns from users and connection dictionaries for conn in collected_conns: if conn.username in server_state.users: if conn in server_state.users[conn.username].connections: server_state.users[conn.username].connections.remove(conn) if conn.id in server_state.connections: del server_state.connections[conn.id] # make sure connection is closed after we garbage # collected it from our list conn.queue = None if conn.socket: try: conn.socket.close() except Exception: raise log.debug("gc_conns() removed:%s time %s" % (len(collected_conns), datetime.utcnow() - start_time))
def closed(self, code, reason=""): server_state = get_state() self.environ.pop("ws4py.app") found_conn = self.conn_id in server_state.connections if hasattr(self, "conn_id") and found_conn: connection = server_state.connections[self.conn_id] connection.mark_for_gc()
def closed(self, code, reason=""): server_state = get_state() self.environ.pop("ws4py.app") found_conn = self.conn_id in server_state.connections if hasattr(self, "conn_id") and found_conn: connection = server_state.connections[self.conn_id] connection.mark_for_gc()
def test_good_json_no_channel(self, dummy_request): from channelstream.wsgi_views.server import message server_state = get_state() channel = Channel("test") channel.store_history = True server_state.channels[channel.name] = channel msg_payload = { "type": "message", "user": "******", "channel": "test", "message": { "text": "test" }, } dummy_request.json_body = [msg_payload] assert server_state.stats["total_unique_messages"] == 0 assert len(channel.history) == 0 message(dummy_request) # change context gevent.sleep(0) assert server_state.stats["total_unique_messages"] == 1 assert len(channel.history) == 1 msg = channel.history[0] assert msg["uuid"] is not None assert msg["user"] == msg_payload["user"] assert msg["message"] == msg_payload["message"] assert msg["type"] == msg_payload["type"] assert msg["channel"] == msg_payload["channel"] assert msg["timestamp"] is not None
def get_info(self, include_history=True, include_users=False): server_state = get_state() settings = {k: getattr(self, k) for k in self.config_keys} chan_info = { "uuid": self.uuid, "name": self.name, "long_name": self.long_name, "settings": settings, "history": self.history if include_history else [], "last_active": self.last_active, "total_connections": sum( [len(conns) for conns in self.connections.values()] ), "total_users": 0, "users": [], } for username in self.connections.keys(): user_inst = server_state.users.get(username) if include_users and user_inst.username not in chan_info["users"]: chan_info["users"].append(user_inst.username) chan_info["users"] = sorted(chan_info["users"]) chan_info["total_users"] = len(chan_info["users"]) return chan_info
def listen(request): """ Handles long polling connections --- get: tags: - "Client API" summary: "Handles long polling connections" description: "" operationId: "listen" produces: - "application/json" responses: 200: description: "Success" """ server_state = get_state() config = request.registry.settings conn_id = utils.uuid_from_string(request.params.get("conn_id")) connection = server_state.connections.get(conn_id) if not connection: raise HTTPUnauthorized() # attach a queue to connection connection.queue = Queue() connection.deliver_catchup_messages() request.response.app_iter = yield_response(request, connection, config) return request.response
def listen(request): """ Handles long polling connections --- get: tags: - "Client API" summary: "Handles long polling connections" description: "" operationId: "listen" produces: - "application/json" responses: 200: description: "Success" """ server_state = get_state() config = request.registry.settings conn_id = utils.uuid_from_string(request.params.get("conn_id")) connection = server_state.connections.get(conn_id) if not connection: raise HTTPUnauthorized() # attach a queue to connection connection.queue = Queue() connection.deliver_catchup_messages() request.response.app_iter = yield_response(request, connection, config) return request.response
def test_good_json_no_channel(self, dummy_request): from channelstream.wsgi_views.server import message server_state = get_state() channel = Channel("test") channel.store_history = True server_state.channels[channel.name] = channel msg_payload = { "type": "message", "user": "******", "channel": "test", "message": {"text": "test"}, } dummy_request.json_body = [msg_payload] assert server_state.stats["total_unique_messages"] == 0 assert len(channel.history) == 0 message(dummy_request) # change context gevent.sleep(0) assert server_state.stats["total_unique_messages"] == 1 assert len(channel.history) == 1 msg = channel.history[0] assert msg["uuid"] is not None assert msg["user"] == msg_payload["user"] assert msg["message"] == msg_payload["message"] assert msg["type"] == msg_payload["type"] assert msg["channel"] == msg_payload["channel"] assert msg["timestamp"] is not None
def test_good_json(self, dummy_request, test_uuids): server_state = get_state() from channelstream.wsgi_views.server import connect dummy_request.json_body = { "username": "******", "conn_id": str(test_uuids[1]), "fresh_user_state": {"key": "foo"}, "user_state": {"bar": "baz"}, "state_public_keys": ["bar"], "channels": ["a", "aB"], "channel_configs": {"a": {"store_history": True, "history_size": 2}}, } assert server_state.channels == {} result = connect(dummy_request) assert len(server_state.channels.keys()) == 2 assert "username" in server_state.users assert test_uuids[1] in server_state.connections assert result["channels"] == ["a", "aB"] assert result["state"] == {"bar": "baz", "key": "foo"} assert result["conn_id"] == test_uuids[1] channels_info = result["channels_info"]["channels"] assert len(channels_info.keys()) == 2 assert channels_info["a"]["total_users"] == 1 assert channels_info["a"]["total_connections"] == 1 assert channels_info["a"]["users"] == ["username"] assert channels_info["a"]["history"] == [] assert result["channels_info"]["users"] == [ {"state": {"bar": "baz", "key": "foo"}, "user": "******"} ]
def gc_conns(): server_state = get_state() with server_state.lock: start_time = datetime.utcnow() threshold = start_time - timedelta(seconds=15) collected_conns = [] # collect every ref in chanels # remove connections from channels for channel in server_state.channels.values(): for username, conns in list(channel.connections.items()): for conn in conns: if conn.last_active < threshold: channel.connections[username].remove(conn) collected_conns.append(conn) channel.after_parted(username) # remove old conns from users and connection dictionaries for conn in collected_conns: if conn.username in server_state.users: if conn in server_state.users[conn.username].connections: server_state.users[conn.username].connections.remove(conn) if conn.id in server_state.connections: del server_state.connections[conn.id] # make sure connection is closed after we garbage # collected it from our list conn.queue = None if conn.socket: try: conn.socket.close() except Exception: raise log.debug( "gc_conns() removed:%s time %s" % (len(collected_conns), datetime.utcnow() - start_time) )
def test_good_json_no_channel(self, dummy_request): from channelstream.wsgi_views.server import message, messages_patch server_state = get_state() channel = Channel("test") channel.store_history = True server_state.channels[channel.name] = channel msg_payload = {"user": "******", "channel": "test", "message": {"text": "test"}} dummy_request.json_body = [msg_payload] message(dummy_request) # change context gevent.sleep(0) msg = channel.history[0] assert msg["message"] == msg_payload["message"] edit_payload = { "uuid": msg["uuid"], "user": "******", "channel": "test", "timestamp": "2010-01-01T01:01", "edited": "2010-01-01T01:02", "message": {"text": "edited_message"}, } dummy_request.json_body = [edit_payload] response = messages_patch(dummy_request)[0] gevent.sleep(0) assert msg["user"] == response["user"] assert msg["message"] == response["message"] assert msg["edited"] == response["edited"] assert msg["timestamp"] == response["timestamp"] frame = channel.frames[0][1] assert id(frame) == id(msg) assert frame["user"] == response["user"] assert frame["message"] == response["message"] assert frame["edited"] == response["edited"] assert frame["timestamp"] == response["timestamp"]
def get_channels(self): server_state = get_state() channels = [] for channel in server_state.channels.values(): if channel.connections.get(self.username): channels.append(channel) return channels
def subscribe(connection=None, channels=None, channel_configs=None): """ :param connection: :param channels: :param channel_configs: :return: """ server_state = get_state() user = server_state.users.get(connection.username) subscribed_to = [] with server_state.lock: if user: for channel_name in channels: if channel_name not in server_state.channels: channel = Channel( channel_name, channel_config=channel_configs.get(channel_name) ) server_state.channels[channel_name] = channel is_found = server_state.channels[channel_name].add_connection( connection ) if is_found: subscribed_to.append(channel_name) return subscribed_to
def pass_message(msg, stats): """ :param msg: :param stats: :return: """ server_state = get_state() msg["catchup"] = False msg["edited"] = None msg["type"] = "message" total_sent = 0 stats["total_unique_messages"] += 1 if msg.get("channel"): channel_inst = server_state.channels.get(msg["channel"]) if channel_inst: total_sent += channel_inst.add_message( msg, pm_users=msg["pm_users"], exclude_users=msg["exclude_users"] ) elif msg["pm_users"]: # if pm then iterate over all users and notify about new message! for username in msg["pm_users"]: user_inst = server_state.users.get(username) if user_inst: total_sent += user_inst.add_message(msg) stats["total_messages"] += total_sent
def test_presence_message_w_users(self, test_uuids): server_state = get_state() user = User("test_user") user.state_from_dict({"key": "1", "key2": "2"}) user.state_public_keys = ["key2"] server_state.users[user.username] = user connection = Connection("test_user", conn_id=test_uuids[1]) user.add_connection(connection) user2 = User("test_user2") user2.state_from_dict({"key": "1", "key2": "2"}) server_state.users[user2.username] = user2 connection2 = Connection("test_user2", conn_id=test_uuids[2]) user2.add_connection(connection2) config = { "notify_presence": True, "broadcast_presence_with_user_lists": True } channel = Channel("test", channel_config=config) channel.add_connection(connection) channel.add_connection(connection2) payload = channel.send_notify_presence_info("test_user", "join") assert len(payload["users"]) == 2 sorted_users = sorted(payload["users"], key=lambda x: x["user"]) assert sorted_users == [ { "state": { "key2": "2" }, "user": "******" }, { "state": {}, "user": "******" }, ]
def get_channels(self): server_state = get_state() channels = [] for channel in server_state.channels.values(): if channel.connections.get(self.username): channels.append(channel) return channels
def get_info(self, include_history=True, include_users=False): server_state = get_state() settings = {k: getattr(self, k) for k in self.config_keys} chan_info = { "uuid": self.uuid, "name": self.name, "long_name": self.long_name, "settings": settings, "history": self.history if include_history else [], "last_active": self.last_active, "total_connections": sum([len(conns) for conns in self.connections.values()]), "total_users": 0, "users": [], } for username in self.connections.keys(): user_inst = server_state.users.get(username) if include_users and user_inst.username not in chan_info["users"]: chan_info["users"].append(user_inst.username) chan_info["users"] = sorted(chan_info["users"]) chan_info["total_users"] = len(chan_info["users"]) return chan_info
def test_good_json_no_channel(self, dummy_request): from channelstream.wsgi_views.server import message, messages_delete server_state = get_state() channel = Channel("test") channel.store_history = True server_state.channels[channel.name] = channel msg_payload = { "user": "******", "channel": "test", "message": { "text": "test" } } dummy_request.json_body = [msg_payload] message(dummy_request) # change context gevent.sleep(0) msg = channel.history[0] assert msg["message"] == msg_payload["message"] dummy_request.json_body = [{ "uuid": str(msg["uuid"]), "channel": "test" }] response = messages_delete(dummy_request) gevent.sleep(0) assert response[0]["uuid"] == msg["uuid"] assert len(channel.history) == 0 assert len(channel.frames) == 1 assert channel.frames[0][1]["type"] == "message:delete"
def test_empty_json(self, dummy_request): from channelstream.wsgi_views.server import message server_state = get_state() dummy_request.json_body = {} assert server_state.stats["total_unique_messages"] == 0 result = message(dummy_request) assert server_state.stats["total_unique_messages"] == 0
def shared_messages(request): server_state = get_state() schema = schemas.MessageBodySchema(context={"request": request}, many=True) data = schema.load(request.json_body).data data = [m for m in data if m.get("channel") or m.get("pm_users")] for msg in data: gevent.spawn(operations.pass_message, msg, server_state.stats) return list(data)
def shared_messages(request): server_state = get_state() schema = schemas.MessageBodySchema(context={"request": request}, many=True) data = schema.load(request.json_body).data data = [m for m in data if m.get("channel") or m.get("pm_users")] for msg in data: gevent.spawn(operations.pass_message, msg, server_state.stats) return list(data)
def gc_users(): server_state = get_state() with server_state.lock: start_time = datetime.utcnow() threshold = datetime.utcnow() - timedelta(days=1) for user in list(six.itervalues(server_state.users)): if user.last_active < threshold: server_state.users.pop(user.username) log.debug("gc_users() time %s" % (datetime.utcnow() - start_time))
def get_channel_info( self, req_channels=None, include_history=True, include_connections=False, include_users=False, exclude_channels=None, return_public_state=False, ): """ Gets channel information for req_channels or all channels if req_channels is None :param: include_history (bool) will include message history for the channel :param: include_connections (bool) will include connection list for users :param: include_users (bool) will include user list for the channel :param: exclude_channels (bool) will exclude specific channels from info list (handy to exclude global broadcast) """ server_state = get_state() if not exclude_channels: exclude_channels = [] start_time = datetime.utcnow() json_data = {"channels": {}, "users": []} users_to_list = set() # select everything for empty list if req_channels is None: channel_instances = six.itervalues(server_state.channels) else: channel_instances = [ server_state.channels[c] for c in req_channels if c in server_state.channels ] for channel_inst in channel_instances: if channel_inst.name in exclude_channels: continue channel_info = channel_inst.get_info( include_history=include_history, include_users=include_users) json_data["channels"][channel_inst.name] = channel_info users_to_list.update(channel_info["users"]) for username in users_to_list: user = server_state.users[username] json_data["users"].append({ "user": username, "state": user.state if not return_public_state else user.public_state, }) log.info("info time: %s" % (datetime.utcnow() - start_time)) return json_data
def test_user_single_assignment(self, test_uuids): server_state = get_state() user = User("test_user") connection = Connection("test_user", conn_id=test_uuids[1]) user.add_connection(connection) channel = Channel("test") server_state.channels[channel.name] = channel channel.add_connection(connection) assert [channel] == user.get_channels()
def received_message(self, m): server_state = get_state() # this is to allow client heartbeats if self.conn_id in server_state.connections: connection = server_state.connections[self.conn_id] connection.mark_activity() user = server_state.users.get(connection.username) if user: user.mark_activity()
def test_user_single_assignment(self, test_uuids): server_state = get_state() user = User("test_user") connection = Connection("test_user", conn_id=test_uuids[1]) user.add_connection(connection) channel = Channel("test") server_state.channels[channel.name] = channel channel.add_connection(connection) assert [channel] == user.get_channels()
def received_message(self, m): server_state = get_state() # this is to allow client heartbeats if self.conn_id in server_state.connections: connection = server_state.connections[self.conn_id] connection.mark_activity() user = server_state.users.get(connection.username) if user: user.mark_activity()
def test_remove_connection_w_presence(self, test_uuids): server_state = get_state() user = User("test_user") server_state.users[user.username] = user connection = Connection("test_user", conn_id=test_uuids[1]) user.add_connection(connection) config = {"notify_presence": True, "broadcast_presence_with_user_lists": True} channel = Channel("test", channel_config=config) channel.add_connection(connection) channel.remove_connection(connection)
def test_empty_json(self, dummy_request): from channelstream.wsgi_views.server import message server_state = get_state() dummy_request.json_body = {} assert server_state.stats["total_unique_messages"] == 0 with pytest.raises(marshmallow.exceptions.ValidationError) as excinfo: message(dummy_request) assert excinfo.value.messages == {"_schema": ["Invalid input type."]}
def cleanup_globals(): server_state = get_state() server_state.channels = {} server_state.connections = {} server_state.users = {} server_state.stats = { "total_messages": 0, "total_unique_messages": 0, "started_on": datetime.utcnow(), }
def test_empty_json(self, dummy_request): from channelstream.wsgi_views.server import message server_state = get_state() dummy_request.json_body = {} assert server_state.stats["total_unique_messages"] == 0 with pytest.raises(marshmallow.exceptions.ValidationError) as excinfo: message(dummy_request) assert excinfo.value.messages == {"_schema": ["Invalid input type."]}
def test_catchup_messages(self, dummy_request): from channelstream.wsgi_views.server import message, connect server_state = get_state() dummy_request.json_body = { "username": "******", "channels": ["test"], "channel_configs": { "test": { "store_history": True, "history_size": 2 } }, } connect(dummy_request) msg_payload = { "type": "message", "user": "******", "channel": "test", "message": { "text": "test3" }, } dummy_request.json_body = [msg_payload] message(dummy_request) # add pm message to non-existing user wrong_user_msg_payload = { "type": "message", "user": "******", "channel": "test", "message": { "text": "test1" }, "pm_users": ["test2"], } msg_payload = { "type": "message", "user": "******", "channel": "test", "message": { "text": "test2" }, "pm_users": ["test1"], } dummy_request.json_body = [wrong_user_msg_payload, msg_payload] message(dummy_request) # change context gevent.sleep(0) connection = server_state.users["test1"].connections[0] messages = connection.get_catchup_messages() assert len(messages) == 2 assert messages[0]["timestamp"] > connection.last_active assert messages[0]["message"]["text"] == "test3" assert messages[1]["timestamp"] > connection.last_active assert messages[1]["message"]["text"] == "test2"
def test_users_active(self): server_state = get_state() user = User("test_user") server_state.users[user.username] = user user2 = User("test_user2") server_state.users[user2.username] = user2 channelstream.gc.gc_users() assert len(server_state.users.items()) == 2 user.last_active -= timedelta(days=2) channelstream.gc.gc_users() assert len(server_state.users.items()) == 1
def test_users_active(self): server_state = get_state() user = User("test_user") server_state.users[user.username] = user user2 = User("test_user2") server_state.users[user2.username] = user2 channelstream.gc.gc_users() assert len(server_state.users.items()) == 2 user.last_active -= timedelta(days=2) channelstream.gc.gc_users() assert len(server_state.users.items()) == 1
def opened(self): server_state = get_state() self.qs = parse_qs(self.environ["QUERY_STRING"]) self.conn_id = utils.uuid_from_string(self.qs.get("conn_id")[0]) if self.conn_id not in server_state.connections: # close connection instantly if user played with id self.close() else: # attach a socket to connection connection = server_state.connections[self.conn_id] connection.socket = self connection.deliver_catchup_messages()
def channels(self): """ Return list of channels names connection belongs to :return: """ server_state = get_state() found_channels = [] for channel in six.itervalues(server_state.channels): user_conns = channel.connections.get(self.username) or [] if self in user_conns: found_channels.append(channel.name) return sorted(found_channels)
def disconnect(conn_id): """ :param conn_id: :return: """ server_state = get_state() conn = server_state.connections.get(conn_id) if conn is not None: conn.mark_for_gc() return True return False
def opened(self): server_state = get_state() self.qs = parse_qs(self.environ["QUERY_STRING"]) self.conn_id = utils.uuid_from_string(self.qs.get("conn_id")[0]) if self.conn_id not in server_state.connections: # close connection instantly if user played with id self.close() else: # attach a socket to connection connection = server_state.connections[self.conn_id] connection.socket = self connection.deliver_catchup_messages()
def channels(self): """ Return list of channels names connection belongs to :return: """ server_state = get_state() found_channels = [] for channel in server_state.channels.values(): user_conns = channel.connections.get(self.username) or [] if self in user_conns: found_channels.append(channel.name) return sorted(found_channels)
def gc_users(): server_state = get_state() with server_state.lock: counter = 0 start_time = datetime.utcnow() threshold = datetime.utcnow() - timedelta(days=1) for user in list(server_state.users.values()): if user.last_active < threshold: counter += 1 server_state.users.pop(user.username) log.debug("gc_users() removed:%s time %s" % (counter, datetime.utcnow() - start_time))
def test_remove_connection_w_presence(self, test_uuids): server_state = get_state() user = User("test_user") server_state.users[user.username] = user connection = Connection("test_user", conn_id=test_uuids[1]) user.add_connection(connection) config = { "notify_presence": True, "broadcast_presence_with_user_lists": True } channel = Channel("test", channel_config=config) channel.add_connection(connection) channel.remove_connection(connection)
def gc_users(): server_state = get_state() with server_state.lock: counter = 0 start_time = datetime.utcnow() threshold = datetime.utcnow() - timedelta(days=1) for user in list(server_state.users.values()): if user.last_active < threshold: counter += 1 server_state.users.pop(user.username) log.debug( "gc_users() removed:%s time %s" % (counter, datetime.utcnow() - start_time) )
def get_catchup_messages(self): server_state = get_state() messages = [] # return catchup messages for channels for channel in self.channels: channel_inst = server_state.channels[channel] messages.extend( channel_inst.get_catchup_frames(self.last_active, self.username)) # and users messages.extend(server_state.users[self.username].get_catchup_frames( self.last_active)) return messages
def get_catchup_messages(self): server_state = get_state() messages = [] # return catchup messages for channels for channel in self.channels: channel_inst = server_state.channels[channel] messages.extend( channel_inst.get_catchup_frames(self.last_active, self.username) ) # and users messages.extend( server_state.users[self.username].get_catchup_frames(self.last_active) ) return messages
def subscribe(request): """ Subscribe view --- post: security: - APIKeyHeader: [] tags: - "Legacy API" summary: "Subscribes connection to new channels" description: "" operationId: "subscribe" consumes: - "application/json" produces: - "application/json" parameters: - in: "body" name: "body" description: "Request JSON body" required: true schema: $ref: "#/definitions/SubscribeBody" responses: 422: description: "Unprocessable Entity" 200: description: "Success" """ server_state = get_state() shared_utils = SharedUtils(request) schema = schemas.SubscribeBodySchema(context={"request": request}) json_body = schema.load(request.json_body).data connection = server_state.connections.get(json_body["conn_id"]) channels = json_body["channels"] channel_configs = json_body.get("channel_configs", {}) subscribed_to = operations.subscribe(connection=connection, channels=channels, channel_configs=channel_configs) # get info config for channel information current_channels = connection.channels channels_info = shared_utils.get_common_info(current_channels, json_body["info"]) return { "channels": current_channels, "channels_info": channels_info, "subscribed_to": sorted(subscribed_to), }
def info(request): """ Returns channel information --- post: security: - APIKeyHeader: [] tags: - "API" summary: "Returns channel information" description: "" operationId: "info" consumes: - "application/json" produces: - "application/json" parameters: - in: "body" name: "body" description: "Request JSON body" schema: $ref: "#/definitions/ChannelInfoBody" responses: 422: description: "Unprocessable Entity" 200: description: "Success" """ server_state = get_state() shared_utils = SharedUtils(request) if not request.body: req_channels = server_state.channels.keys() info_config = { "include_history": True, "include_users": True, "exclude_channels": [], "include_connections": True, } else: schema = schemas.ChannelInfoBodySchema(context={"request": request}) data = schema.load(request.json_body).data # get info config for channel information info_config = data.get("info") or {} req_channels = info_config.get("channels", None) info_config["include_connections"] = info_config.get( "include_connections", True ) channels_info = shared_utils.get_common_info(req_channels, info_config) return channels_info
def test_add_connection_w_presence(self): server_state = get_state() user = User("test_user") server_state.users[user.username] = user connection = Connection("test_user", conn_id=test_uuids[1]) user.add_connection(connection) config = { "notify_presence": True, "broadcast_presence_with_user_lists": True } channel = Channel("test", channel_config=config) channel.add_connection(connection) assert len(channel.connections["test_user"]) == 1 assert "test_user" in channel.connections assert connection in channel.connections["test_user"]
def subscribe(request): """ Subscribe view --- post: security: - APIKeyHeader: [] tags: - "API" summary: "Subscribes connection to new channels" description: "" operationId: "subscribe" consumes: - "application/json" produces: - "application/json" parameters: - in: "body" name: "body" description: "Request JSON body" required: true schema: $ref: "#/definitions/SubscribeBody" responses: 422: description: "Unprocessable Entity" 200: description: "Success" """ server_state = get_state() shared_utils = SharedUtils(request) schema = schemas.SubscribeBodySchema(context={"request": request}) json_body = schema.load(request.json_body).data connection = server_state.connections.get(json_body["conn_id"]) channels = json_body["channels"] channel_configs = json_body.get("channel_configs", {}) subscribed_to = operations.subscribe( connection=connection, channels=channels, channel_configs=channel_configs ) # get info config for channel information current_channels = connection.channels channels_info = shared_utils.get_common_info(current_channels, json_body["info"]) return { "channels": current_channels, "channels_info": channels_info, "subscribed_to": sorted(subscribed_to), }
def info(request): """ Returns channel information --- post: security: - APIKeyHeader: [] tags: - "Legacy API" summary: "Returns channel information" description: "" operationId: "info" consumes: - "application/json" produces: - "application/json" parameters: - in: "body" name: "body" description: "Request JSON body" schema: $ref: "#/definitions/ChannelInfoBody" responses: 422: description: "Unprocessable Entity" 200: description: "Success" """ server_state = get_state() shared_utils = SharedUtils(request) if not request.body: req_channels = server_state.channels.keys() info_config = { "include_history": True, "include_users": True, "exclude_channels": [], "include_connections": True, } else: schema = schemas.ChannelInfoBodySchema(context={"request": request}) data = schema.load(request.json_body).data # get info config for channel information info_config = data.get("info") or {} req_channels = info_config.get("channels", None) info_config["include_connections"] = info_config.get( "include_connections", True) channels_info = shared_utils.get_common_info(req_channels, info_config) return channels_info
def delete_message(msg): """ :param msg: :return: """ server_state = get_state() if msg.get("channel"): channel_inst = server_state.channels.get(msg["channel"]) if channel_inst: channel_inst.delete_message(msg) elif msg["pm_users"]: # if pm then iterate over all users and notify about new message! for username in msg["pm_users"]: user_inst = server_state.users.get(username) if user_inst: user_inst.delete_message(msg)
def test_user_multi_assignment(self, test_uuids): server_state = get_state() user = User("test_user") connection = Connection("test_user", conn_id=test_uuids[1]) connection2 = Connection("test_user", conn_id=test_uuids[2]) connection3 = Connection("test_user", conn_id=test_uuids[3]) user.add_connection(connection) user.add_connection(connection2) user.add_connection(connection3) channel = Channel("test") channel2 = Channel("test2") server_state.channels[channel.name] = channel server_state.channels[channel2.name] = channel2 channel.add_connection(connection) channel.add_connection(connection2) channel2.add_connection(connection3) assert ["test", "test2"] == sorted([c.name for c in user.get_channels()])
def connect( username=None, fresh_user_state=None, state_public_keys=None, update_user_state=None, conn_id=None, channels=None, channel_configs=None, ): """ :param username: :param fresh_user_state: :param state_public_keys: :param update_user_state: :param conn_id: :param channels: :param channel_configs: :return: """ server_state = get_state() with server_state.lock: if username not in server_state.users: user = User(username) user.state_from_dict(fresh_user_state) server_state.users[username] = user else: user = server_state.users[username] if state_public_keys is not None: user.state_public_keys = state_public_keys user.state_from_dict(update_user_state) connection = Connection(username, conn_id) if connection.id not in server_state.connections: server_state.connections[connection.id] = connection user.add_connection(connection) for channel_name in channels: # user gets assigned to a channel if channel_name not in server_state.channels: channel = Channel( channel_name, channel_config=channel_configs.get(channel_name) ) server_state.channels[channel_name] = channel server_state.channels[channel_name].add_connection(connection) log.info("connecting %s with uuid %s" % (username, connection.id)) return connection, user
def set_channel_config(channel_configs): """ :param channel_configs: :return: """ server_state = get_state() with server_state.lock: for channel_name, config in channel_configs.items(): if not server_state.channels.get(channel_name): channel = Channel( channel_name, channel_config=channel_configs.get(channel_name) ) server_state.channels[channel_name] = channel else: channel = server_state.channels[channel_name] channel.reconfigure_from_dict(channel_configs.get(channel_name))
def test_gc_connections_collecting(self, test_uuids): server_state = get_state() channel = Channel("test") server_state.channels[channel.name] = channel channel2 = Channel("test2") server_state.channels[channel2.name] = channel2 user = User("test_user") server_state.users[user.username] = user user2 = User("test_user2") server_state.users[user2.username] = user2 connection = Connection("test_user", test_uuids[1]) server_state.connections[connection.id] = connection connection2 = Connection("test_user", test_uuids[2]) connection2.mark_for_gc() server_state.connections[connection2.id] = connection2 connection3 = Connection("test_user2", test_uuids[3]) connection3.mark_for_gc() server_state.connections[connection3.id] = connection3 connection4 = Connection("test_user2", test_uuids[4]) server_state.connections[connection4.id] = connection4 user.add_connection(connection) user.add_connection(connection2) channel.add_connection(connection) channel.add_connection(connection2) user2.add_connection(connection3) user2.add_connection(connection4) channel2.add_connection(connection3) channel2.add_connection(connection4) channelstream.gc.gc_conns() assert len(server_state.connections.items()) == 2 conns = server_state.channels["test"].connections["test_user"] assert len(conns) == 1 assert conns == [connection] conns = server_state.channels["test2"].connections["test_user2"] assert len(conns) == 1 assert conns == [connection4] assert len(user.connections) == 1 assert len(user2.connections) == 1 connection.mark_for_gc() connection4.mark_for_gc() channelstream.gc.gc_conns() assert "test_user" not in server_state.channels["test"].connections assert "test_user2" not in server_state.channels["test2"].connections assert len(server_state.channels["test"].connections.items()) == 0 assert len(server_state.channels["test2"].connections.items()) == 0
def test_catchup_messages(self, dummy_request): from channelstream.wsgi_views.server import message, connect server_state = get_state() dummy_request.json_body = { "username": "******", "channels": ["test"], "channel_configs": {"test": {"store_history": True, "history_size": 2}}, } connect(dummy_request) msg_payload = { "type": "message", "user": "******", "channel": "test", "message": {"text": "test3"}, } dummy_request.json_body = [msg_payload] message(dummy_request) # add pm message to non-existing user wrong_user_msg_payload = { "type": "message", "user": "******", "channel": "test", "message": {"text": "test1"}, "pm_users": ["test2"], } msg_payload = { "type": "message", "user": "******", "channel": "test", "message": {"text": "test2"}, "pm_users": ["test1"], } dummy_request.json_body = [wrong_user_msg_payload, msg_payload] message(dummy_request) # change context gevent.sleep(0) connection = server_state.users["test1"].connections[0] messages = connection.get_catchup_messages() assert len(messages) == 2 assert messages[0]["timestamp"] > connection.last_active assert messages[0]["message"]["text"] == "test3" assert messages[1]["timestamp"] > connection.last_active assert messages[1]["message"]["text"] == "test2"