Beispiel #1
0
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))
Beispiel #2
0
 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()
Beispiel #3
0
 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()
Beispiel #4
0
    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
Beispiel #5
0
    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
Beispiel #6
0
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
Beispiel #7
0
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": "******"}
        ]
Beispiel #10
0
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)
        )
Beispiel #11
0
    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"]
Beispiel #12
0
 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
Beispiel #13
0
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
Beispiel #14
0
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
Beispiel #15
0
 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": "******"
         },
     ]
Beispiel #16
0
 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
Beispiel #17
0
    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
Beispiel #18
0
    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"
Beispiel #19
0
    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
Beispiel #20
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)
Beispiel #21
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)
Beispiel #22
0
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))
Beispiel #23
0
    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
Beispiel #24
0
 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()
Beispiel #25
0
 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()
Beispiel #26
0
 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()
Beispiel #27
0
 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()
Beispiel #28
0
 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)
Beispiel #29
0
    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."]}
Beispiel #30
0
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(),
    }
Beispiel #31
0
    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."]}
Beispiel #32
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"
Beispiel #33
0
 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
Beispiel #34
0
 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
Beispiel #35
0
 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()
Beispiel #36
0
 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)
Beispiel #37
0
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
Beispiel #38
0
 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()
Beispiel #39
0
 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)
Beispiel #40
0
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))
Beispiel #41
0
 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)
Beispiel #42
0
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)
        )
Beispiel #43
0
 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
Beispiel #44
0
 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
Beispiel #45
0
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),
    }
Beispiel #46
0
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
Beispiel #47
0
 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"]
Beispiel #48
0
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),
    }
Beispiel #49
0
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
Beispiel #50
0
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)
Beispiel #51
0
 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()])
Beispiel #52
0
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
Beispiel #53
0
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))
Beispiel #54
0
 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
Beispiel #55
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"