Пример #1
0
 def recv(self):
     """Receive a SockJS wrapped msg."""
     raw = self.websocket.recv()
     if not raw.startswith('a'):
         raise ValueError('Invalid response: %r' % raw)
     wrapped = ejson.loads(raw[1:])
     return [ejson.loads(msg) for msg in wrapped]
Пример #2
0
    async def _handle_messages(
            self, websocket: websockets.protocol.WebSocketCommonProtocol,
            _path: str):
        async def send(data: object) -> None:
            await websocket.send(ejson.dumps(data))

        async def fail():
            await send({
                "msg": "failed",
                "version": "1",
            })

        data = ejson.loads(await websocket.recv())
        if data["msg"] != "connect":
            return await fail()
        await websocket.send(
            ejson.dumps({
                "msg": "connected",
                "session": str(uuid.uuid4())
            }))
        async for message in websocket:
            data = ejson.loads(message)
            if data["msg"] == "method":
                assert data["method"] in self._method_handlers
                await send({
                    "id":
                    data["id"],
                    "msg":
                    "result",
                    "result":
                    self._method_handlers[data["method"]](*data["params"]),
                })
                continue
            await fail()
Пример #3
0
 def ddp_frames_from_message(self, message):
     """Yield DDP messages from a raw WebSocket message."""
     # parse message set
     try:
         msgs = ejson.loads(message)
     except ValueError:
         self.reply("error", error=400, reason="Data is not valid EJSON")
         raise StopIteration
     if not isinstance(msgs, list):
         self.reply("error", error=400, reason="Invalid EJSON messages")
         raise StopIteration
     # process individual messages
     while msgs:
         # pop raw message from the list
         raw = msgs.pop(0)
         # parse message payload
         try:
             data = ejson.loads(raw)
         except (TypeError, ValueError):
             data = None
         if not isinstance(data, dict):
             self.reply("error", error=400, reason="Invalid SockJS DDP payload", offendingMessage=raw)
         yield data
         if msgs:
             # yield to other greenlets before processing next msg
             gevent.sleep()
Пример #4
0
 def recv(self):
     """Receive a SockJS wrapped msg."""
     raw = self.websocket.recv()
     if not raw.startswith('a'):
         raise ValueError('Invalid response: %r' % raw)
     wrapped = ejson.loads(raw[1:])
     return [ejson.loads(msg) for msg in wrapped]
Пример #5
0
 def ddp_frames_from_message(self, message):
     """Yield DDP messages from a raw WebSocket message."""
     # parse message set
     try:
         msgs = ejson.loads(message)
     except ValueError:
         self.reply(
             'error', error=400, reason='Data is not valid EJSON',
         )
         raise StopIteration
     if not isinstance(msgs, list):
         self.reply(
             'error', error=400, reason='Invalid EJSON messages',
         )
         raise StopIteration
     # process individual messages
     while msgs:
         # pop raw message from the list
         raw = msgs.pop(0)
         # parse message payload
         try:
             data = ejson.loads(raw)
         except (TypeError, ValueError):
             data = None
         if not isinstance(data, dict):
             self.reply(
                 'error', error=400,
                 reason='Invalid SockJS DDP payload',
                 offendingMessage=raw,
             )
         yield data
         if msgs:
             # yield to other greenlets before processing next msg
             gevent.sleep()
Пример #6
0
 def test_binary(self):
     if six.PY3:
         s = ejson.dumps([six.binary_type('foo', 'ascii'), 'foo'])
         self.assertEqual('[{"$binary": "Zm9v"}, "foo"]', s)
         self.assertEqual([six.binary_type('foo', 'ascii'), 'foo'], ejson.loads(s))
     else:
         s = ejson.dumps(['foo', six.text_type('foo')])
         self.assertEqual('["foo", "foo"]', s)
         self.assertEqual(['foo', 'foo'], ejson.loads('[{"$binary": "Zm9v"}, "foo"]'))
Пример #7
0
    async def _handle_messages(self, websocket: WebSocketServerProtocol,
                               _path: str):
        async def send(data: object) -> None:
            await websocket.send(ejson.dumps(data))

        self._subscription_task = asyncio.create_task(
            self._send_subscription_messages(send=send, ))

        async def fail():
            await send({
                "msg": "failed",
                "version": "1",
            })

        data = ejson.loads(await websocket.recv())
        if data["msg"] != "connect":
            return await fail()
        await websocket.send(
            ejson.dumps({
                "msg": "connected",
                "session": str(uuid.uuid4())
            }))
        async for message in websocket:
            data = ejson.loads(message)
            if data["msg"] == "method":
                assert data["method"] in self._method_handlers
                await send({
                    "id":
                    data["id"],
                    "msg":
                    "result",
                    "result":
                    self._method_handlers[data["method"]](*data["params"]),
                })
                continue
            if data["msg"] == "sub":
                self._subscriptions[data["name"]] = data["id"]
                await send({
                    "msg": "ready",
                    "subs": [data["id"]],
                })
                continue
            if data["msg"] == "unsub":
                topic = [
                    topic for topic, id in self._subscriptions.items()
                    if id == data["id"]
                ][0]
                del self._subscriptions[topic]
                # Nothing to respond with in this case.
                continue

            await fail()
Пример #8
0
    def poll(self, conn):
        """Poll DB socket and process async tasks."""
        while 1:
            state = conn.poll()
            if state == psycopg2.extensions.POLL_OK:
                while conn.notifies:
                    notify = conn.notifies.pop()
                    self.logger.info(
                        "Got NOTIFY (pid=%d, payload=%r)",
                        notify.pid, notify.payload,
                    )

                    # read the header and check seq/fin.
                    hdr, chunk = notify.payload.split('|', 1)
                    # print('RECEIVE: %s' % hdr)
                    header = ejson.loads(hdr)
                    uuid = header['uuid']
                    size, chunks = self.chunks.setdefault(uuid, [0, {}])
                    if header['fin']:
                        size = self.chunks[uuid][0] = header['seq']

                    # stash the chunk
                    chunks[header['seq']] = chunk

                    if len(chunks) != size:
                        # haven't got all the chunks yet
                        continue  # process next NOTIFY in loop

                    # got the last chunk -> process it.
                    data = ''.join(
                        chunk for _, chunk in sorted(chunks.items())
                    )
                    del self.chunks[uuid]  # don't forget to cleanup!
                    data = ejson.loads(data)
                    sender = data.pop('_sender', None)
                    tx_id = data.pop('_tx_id', None)
                    for connection_id in data.pop('_connection_ids'):
                        try:
                            websocket = self.connections[connection_id]
                        except KeyError:
                            continue  # connection not in this process
                        if connection_id == sender:
                            websocket.send(data, tx_id=tx_id)
                        else:
                            websocket.send(data)
                break
            elif state == psycopg2.extensions.POLL_WRITE:
                gevent.select.select([], [conn.fileno()], [])
            elif state == psycopg2.extensions.POLL_READ:
                gevent.select.select([conn.fileno()], [], [])
            else:
                self.logger.warn('POLL_ERR: %s', state)
Пример #9
0
    def poll(self, conn):
        """Poll DB socket and process async tasks."""
        while 1:
            state = conn.poll()
            if state == psycopg2.extensions.POLL_OK:
                while conn.notifies:
                    notify = conn.notifies.pop()
                    self.logger.info(
                        "Got NOTIFY (pid=%d, payload=%r)",
                        notify.pid,
                        notify.payload,
                    )

                    # read the header and check seq/fin.
                    hdr, chunk = notify.payload.split('|', 1)
                    # print('RECEIVE: %s' % hdr)
                    header = ejson.loads(hdr)
                    uuid = header['uuid']
                    size, chunks = self.chunks.setdefault(uuid, [0, {}])
                    if header['fin']:
                        size = self.chunks[uuid][0] = header['seq']

                    # stash the chunk
                    chunks[header['seq']] = chunk

                    if len(chunks) != size:
                        # haven't got all the chunks yet
                        continue  # process next NOTIFY in loop

                    # got the last chunk -> process it.
                    data = ''.join(chunk
                                   for _, chunk in sorted(chunks.items()))
                    del self.chunks[uuid]  # don't forget to cleanup!
                    data = ejson.loads(data)
                    sender = data.pop('_sender', None)
                    tx_id = data.pop('_tx_id', None)
                    for connection_id in data.pop('_connection_ids'):
                        try:
                            websocket = self.connections[connection_id]
                        except KeyError:
                            continue  # connection not in this process
                        if connection_id == sender:
                            websocket.send(data, tx_id=tx_id)
                        else:
                            websocket.send(data)
                break
            elif state == psycopg2.extensions.POLL_WRITE:
                gevent.select.select([], [conn.fileno()], [])
            elif state == psycopg2.extensions.POLL_READ:
                gevent.select.select([conn.fileno()], [], [])
            else:
                self.logger.warn('POLL_ERR: %s', state)
Пример #10
0
def test_decimal():
    # Given that I have a decimal object
    obj = decimal.Decimal("0.14285714285714285")

    # When I try to serialize it
    serialized = ejson.dumps(obj)

    # Then I see the proper string description of the object
    serialized.should.contain('"__class__": "decimal.Decimal"')
    serialized.should.contain('"__value__": "0.14285714285714285"')

    # When I try to deserialize, I see that it also works
    ejson.loads(serialized).should.equal(obj)
Пример #11
0
def test_time():
    # Given that I have a time object to serialize
    obj = datetime.time(1, 12, 50, 123)

    # When I serialize it
    serialized = ejson.dumps(obj)

    # Then I see that it was correctly serialized
    serialized.should.contain('"__class__": "datetime.time"')
    serialized.should.contain('"__value__": "01:12:50.000123"')

    # When I try to deserialize, I see that it also works
    ejson.loads(serialized).should.equal(obj)
Пример #12
0
def test_time_with_timezone():
    # Given that I have a time object to serialize
    obj = datetime.time(1, 12, 50, 123,
                        ejson.serializers.TZInfoHelper(-120, "FNT"))

    # When I serialize it
    serialized = ejson.dumps(obj)

    # Then I see that it was correctly serialized
    serialized.should.contain('"__class__": "datetime.time"')
    serialized.should.contain('"__value__": "01:12:50.000123-02:00"')

    # When I try to deserialize, I see that it also works
    ejson.loads(serialized).should.equal(obj)
Пример #13
0
def test_date():
    # Given that I have an instance of the date object that must be serialized
    obj = datetime.date(2013, 0o3, 30)

    # When I serialize it
    serialized = ejson.dumps(obj)

    # Then I see the proper representation of the instance that will allow us
    # to deserialize it later
    serialized.should.contain('"__class__": "datetime.date"')
    serialized.should.contain('"__value__": "2013-03-30"')

    # When I try to load this info again, Then I see that it also works
    ejson.loads(serialized).should.equal(obj)
Пример #14
0
    def on_message(self, message):
        """Process a message received from remote."""
        if self.ws.closed:
            return None
        try:
            self.logger.debug('< %s %r', self, message)

            # parse message set
            try:
                msgs = ejson.loads(message)
            except ValueError as err:
                self.error(400, 'Data is not valid EJSON')
                return
            if not isinstance(msgs, list):
                self.error(400, 'Invalid EJSON messages')
                return

            # process individual messages
            while msgs:
                # parse message payload
                raw = msgs.pop(0)
                try:
                    data = ejson.loads(raw)
                except (TypeError, ValueError) as err:
                    self.error(400, 'Data is not valid EJSON')
                    continue
                if not isinstance(data, dict):
                    self.error(400, 'Invalid EJSON message payload', raw)
                    continue
                try:
                    msg = data.pop('msg')
                except KeyError:
                    self.error(400, 'Bad request', offendingMessage=data)
                    continue
                # dispatch message
                try:
                    self.dispatch(msg, data)
                except MeteorError as err:
                    self.error(err)
                except Exception as err:
                    traceback.print_exc()
                    self.error(err)
                # emit request_finished signal to close DB connections
                signals.request_finished.send(sender=self.__class__)
                if msgs:
                    # yield to other greenlets before processing next msg
                    gevent.sleep()
        except geventwebsocket.WebSocketError as err:
            self.ws.close()
Пример #15
0
    def on_message(self, message):
        """Process a message received from remote."""
        if self.ws.closed:
            return None
        try:
            self.logger.debug('< %s %r', self, message)

            # parse message set
            try:
                msgs = ejson.loads(message)
            except ValueError as err:
                self.error(400, 'Data is not valid EJSON')
                return
            if not isinstance(msgs, list):
                self.error(400, 'Invalid EJSON messages')
                return

            # process individual messages
            while msgs:
                # parse message payload
                raw = msgs.pop(0)
                try:
                    data = ejson.loads(raw)
                except (TypeError, ValueError) as err:
                    self.error(400, 'Data is not valid EJSON')
                    continue
                if not isinstance(data, dict):
                    self.error(400, 'Invalid EJSON message payload', raw)
                    continue
                try:
                    msg = data.pop('msg')
                except KeyError:
                    self.error(400, 'Bad request', offendingMessage=data)
                    continue
                # dispatch message
                try:
                    self.dispatch(msg, data)
                except MeteorError as err:
                    self.error(err)
                except Exception as err:
                    traceback.print_exc()
                    self.error(err)
                # emit request_finished signal to close DB connections
                signals.request_finished.send(sender=self.__class__)
                if msgs:
                    # yield to other greenlets before processing next msg
                    gevent.sleep()
        except geventwebsocket.WebSocketError as err:
            self.ws.close()
Пример #16
0
    def on_message(self, msg):
        msg = ejson.loads(msg)

        if msg.get('msg') == 'added':
            self.on_added(msg['collection'], msg['id'], msg.get('fields', {}))

        if msg.get('msg') == 'changed':
            self.on_changed(msg['collection'], msg['id'],
                            msg.get('fields', {}), msg.get('cleared', []))

        if msg.get('msg') == 'removed':
            self.on_removed(msg['collection'], msg['id'])

        if msg.get('msg') == 'ready':
            self.on_ready(msg['subs'])

        if msg.get('msg') == 'addedBefore':
            raise NotImplementedError

        if msg.get('msg') == 'movedBefore':
            raise NotImplementedError


        if msg.get('msg') == 'result':
            self.on_result(msg['id'], msg.get('error'), msg.get('result', {}))

        if msg.get('msg') == 'updated':
            self.on_updated(msg['methods'])


        return msg
Пример #17
0
    def consumer(self):  # all data gets must go inside a try
        while True:
            msg = yield self.queue.get()
            if msg == 'stop':
                return
            data = ejson.loads(msg)
            message = data['msg']

            if message == 'method':
                if data['method'] not in methods:
                    self.send_nomethod(data['id'], 'method does not exist')
                else:
                    method = getattr(self, data['method'])
                    result = yield method(**data['params'])
                    self.send_result(data['id'], result)
            elif message == 'sub':
                if data['name'] not in subs:
                    self.send_nosub(data['id'], 'sub does not exist')
                else:
                    query = getattr(self, data['name'])(**data['params'])
                    tornado.ioloop.IOLoop.current().spawn_callback(
                        self.feed, data['id'], query)
                #prefixed = 'sub_' + data['name']
                #try:
                #    query = getattr(self, prefixed)(**data['params'])
                #    tornado.ioloop.IOLoop.current().spawn_callback(self.feed, data['id'], query)
                #except AttributeError:
                #    self.send_nosub(data['id'], 'sub does not exist')
            elif message == 'unsub':
                id = data['id']
                feed = self.registered_feeds[id]
                feed.close()
                del self.registered_feeds[id]

            self.queue.task_done()
Пример #18
0
    async def connect(self):
        '''This coroutine establishes a connection to a server.
        It blocks until the connection is established or an exception is raised.
        
        Raises ddp_asyncio.ConnectionError if the server reports a failure (usually caused by incomplatible versions of the DDP protocol.)
        '''

        self._websocket = await websockets.connect(self.url)

        msg = {'msg': 'connect', 'version': '1', 'support': ['1']}

        await self._websocket.send(ejson.dumps(msg))

        while self._websocket.open:
            msg = await self._websocket.recv()
            msg = ejson.loads(msg)
            _type = msg.get('msg')

            if _type == 'failed':
                raise ConnectionError(
                    'The server is not compatible with version 1 of the DDP protocol.'
                )
            elif _type == 'connected':
                # Ensure all Collections are in their default states
                for col in self._cols.values():
                    col._data = {}

                self.is_connected = True
                self._disconnection_event.clear()
                self._event_loop.create_task(self.__handler__())

                return
Пример #19
0
def process(event_name, data):
    """Iterates over the event handler registry and execute each found
    handler.

    It takes the event name and its its `data`, passing the return of
    `ejson.loads(data)` to the found handlers.
    """
    deserialized = loads(data)
    event_cls = find_event(event_name)
    event = event_cls(event_name, deserialized)
    try:
        event.clean()
    except ValidationError as exc:
        if os.environ.get('EVENTLIB_RAISE_ERRORS'):
            raise
        else:
            logger.warning(
                "The event system just got an exception while cleaning "
                "data for the event '{}'\ndata: {}\nexc: {}".format(
                    event_name, data, str(exc)))
            return

    for handler in find_handlers(event_name):
        try:
            handler(deserialized)
        except Exception as exc:
            logger.warning(
                (u'One of the handlers for the event "{}" has failed with the '
                 u'following exception: {}').format(event_name, str(exc)))
            if getsetting('DEBUG'):
                raise exc
    event._broadcast()
Пример #20
0
 def test_date(self):
     s1 = ejson.dumps(datetime.date(2015, 1, 25))
     self.assertEqual('{"$date": 1422144000000}', s1)
     self.assertEqual(datetime.datetime(2015, 1, 25, tzinfo=ejson.timezone.utc), ejson.loads(s1))
     s2 = ejson.dumps(datetime.datetime(2015, 1, 25, 10, 30, 1, tzinfo=ejson.timezone.utc))
     self.assertEqual('{"$date": 1422181801000}', s2)
     self.assertEqual(datetime.datetime(2015, 1, 25, 10, 30, 1, tzinfo=ejson.timezone.utc), ejson.loads(s2))
Пример #21
0
 def poll(self):
     """Poll DB socket and process async tasks."""
     while 1:
         state = self.conn.poll()
         if state == psycopg2.extensions.POLL_OK:
             while self.conn.notifies:
                 notify = self.conn.notifies.pop()
                 name = notify.channel
                 self.logger.info(
                     "Got NOTIFY (pid=%d, name=%r, payload=%r)",
                     notify.pid, name, notify.payload,
                 )
                 try:
                     self._sub_lock.acquire()
                     self.logger.info(self.all_subs)
                     subs = self.all_subs[name]
                     data = ejson.loads(notify.payload)
                     sub_ids = data.pop('_sub_ids')
                     self.logger.info('Subscribers: %r', sub_ids)
                     self.logger.info(subs)
                     for id_, func in subs.items():
                         if id_ not in sub_ids:
                             continue  # not for this subscription
                         gevent.spawn(func, id_, name, data)
                 finally:
                     self._sub_lock.release()
             break
         elif state == psycopg2.extensions.POLL_WRITE:
             gevent.select.select([], [self.conn.fileno()], [])
         elif state == psycopg2.extensions.POLL_READ:
             gevent.select.select([self.conn.fileno()], [], [])
         else:
             self.logger.warn('POLL_ERR: %s', state)
Пример #22
0
def process(event_name, data):
    """Iterates over the event handler registry and execute each found
    handler.

    It takes the event name and its its `data`, passing the return of
    `ejson.loads(data)` to the found handlers.
    """
    deserialized = loads(data)
    event_cls = find_event(event_name)
    event = event_cls(event_name, deserialized)
    try:
        event.clean()
    except ValidationError as exc:
        if os.environ.get('EVENTLIB_RAISE_ERRORS'):
            raise
        else:
            logger.warning(
                "The event system just got an exception while cleaning "
                "data for the event '{}'\ndata: {}\nexc: {}".format(
                    event_name, data, str(exc)))
            return

    for handler in find_handlers(event_name):
        try:
            handler(deserialized)
        except Exception as exc:
            logger.warning(
                (u'One of the handlers for the event "{}" has failed with the '
                 u'following exception: {}').format(event_name, str(exc)))
            if getsetting('DEBUG'):
                raise exc
    event._broadcast()
Пример #23
0
    def recvloop(self):
        while self.websocket.open:
            while len(self.callcache) > 0:
                self.call(*self.callcache.pop(0))

            msg = yield from self.websocket.recv()
            if not msg: continue
            msg = ejson.loads(msg)

            if msg.get('msg') == 'connected':
                self.connected = True
                self.session = msg['session']

            elif msg.get('msg') == 'ping':
                yield from self.websocket.send(
                    ejson.dumps({
                        'msg': 'pong',
                        'id': msg.get('id')
                    }))

            elif msg.get('msg') == 'ready':
                for sub in self.subs.values():
                    if sub.id in msg['subs']:
                        yield from sub.ready_cb(sub)

            elif msg.get('msg') == 'added':
                sub = self.subs.get(msg['collection'])
                if sub:
                    sub.data[msg['id']] = msg['fields']
                    yield from sub.added_cb(sub, msg['id'], msg['fields'])

            elif msg.get('msg') == 'changed':
                sub = self.subs.get(msg['collection'])
                if sub:
                    if msg.get('fields'):
                        sub.data[msg['id']].update(msg['fields'])
                        yield from sub.changed_cb(sub, msg['id'],
                                                  msg['fields'])
                    elif msg.get('cleared'):
                        for key in msg['cleared']:
                            del sub.data[key]
                        yield from sub.changed_cb(sub, msg['id'],
                                                  msg['cleared'])

            elif msg.get('msg') == 'removed':
                sub = self.subs.get(msg['collection'])
                if sub:
                    del sub.data[msg['id']]
                    yield from sub.removed_cb(sub, msg['id'])

            elif msg.get('msg') == 'result':
                c = self.calls.get(msg['id'])
                if c:
                    yield from c.onfinished(msg.get('result'),
                                            msg.get('error'))

        self.connected = False
        while True:
            yield from self.connect()
            return
Пример #24
0
 def sub_unique_objects(self, obj, params=None, pub=None, *args, **kwargs):
     """Return objects that are only visible through given subscription."""
     if params is None:
         params = ejson.loads(obj.params_ejson)
     if pub is None:
         pub = self.get_pub_by_name(obj.publication)
     queries = collections.OrderedDict(
         (col, qs)
         for (qs, col) in (self.qs_and_collection(qs)
                           for qs in pub.user_queries(obj.user, *params)))
     # mergebox via MVCC!  For details on how this is possible, read this:
     # https://devcenter.heroku.com/articles/postgresql-concurrency
     to_send = collections.OrderedDict((
         col,
         col.objects_for_user(user=obj.user_id, qs=qs, *args, **kwargs),
     ) for col, qs in queries.items())
     for other in Subscription.objects.filter(
             connection=obj.connection_id,
             collections__collection_name__in=[col.name for col in queries],
     ).exclude(pk=obj.pk, ).order_by('pk').distinct():
         other_pub = self.get_pub_by_name(other.publication)
         for qs in other_pub.user_queries(other.user, *other.params):
             qs, col = self.qs_and_collection(qs)
             if col not in to_send:
                 continue
             to_send[col] = to_send[col].exclude(
                 pk__in=col.objects_for_user(user=other.user_id,
                                             qs=qs,
                                             *args,
                                             **kwargs).values('pk'), )
     for col, qs in to_send.items():
         yield col, qs.distinct()
Пример #25
0
 def test_escape_escape(self):
     self.assertEqual(
         {
             "$date":
             datetime.datetime(
                 1970, 1, 1, 0, 0, 32, tzinfo=ejson.timezone.utc)
         }, ejson.loads('{"$escape": {"$date": {"$date": 32000}}}'))
Пример #26
0
 def load(self):
     self.clear()
     if os.path.isfile(self._file):
         with file(self._file, 'r') as f:
             raw = f.read()
             if raw:
                 data = ejson.loads(raw)
                 self.update(data)
Пример #27
0
    def _handle_message(self, msg, socket):
        if msg.tp == sockjs.MSG_OPEN:
            socket._ddp_session = None

        elif msg.tp == sockjs.MSG_MESSAGE:

            try:
                try:
                    mbody = ejson.loads(msg.data)
                    if type(mbody) != dict or mbody.get("msg") == None:
                        self.logger.debug(
                            "Discarding non-object DDP message {msg}"
                            .format(msg=msg.data),
                            )
                        socket.send(ejson.dumps({
                            "msg": "error",
                            "reason": "Bad request",
                            "offendingMessage": mbody,
                            }))
                        return
                except ValueError:
                    self.logger.debug(
                        "Discarding message with invalid JSON {msg}"
                        .format(msg=msg.data),
                        )
                    socket.send(json.dumps({
                        "msg": "error",
                        "reason": "Bad request",
                        }))
                    return

                if mbody["msg"] == "connect":
                    if socket._ddp_session:
                        socket.send(json.dumps({
                            "msg": "error",
                            "reason": "Already connected",
                            "offendingMessage": mbody,
                            }))
                        return
                    asyncio.async(self._handle_connect(mbody, socket),
                                  loop=self.loop)
                    return

                if not socket._ddp_session:
                    socket.send(ejson.dumps({
                        "msg": "error",
                        "reason": "Must connect first",
                        "offendingMessage": mbody,
                        }))
                    return

                socket._ddp_session.process_message(mbody)

            except Exception:
                self.logger.error(
                    "Internal exception while processing message {msg}\n{err}"
                    .format(msg=msg.data, err=traceback.format_exc())
                    )
Пример #28
0
 def send(self, data):
     """Send raw `data` to WebSocket client."""
     if data[1:]:
         msg = ejson.loads(data[1:])
     self.logger.debug('> %s %r', self, data)
     try:
         self.ws.send(data)
     except geventwebsocket.WebSocketError:
         self.ws.close()
Пример #29
0
def test_datetime_with_microseconds():
    # Given that I have an instance of the datetime object with timezone
    # information, must be serialized
    obj = datetime.datetime(
        2013, 0o3, 30, 12, 12, 12, 123456,
        ejson.serializers.TZInfoHelper(60 * 3, "America/Sao_Paulo"))

    # When I serialize it
    serialized = ejson.dumps(obj)

    # Then I see the proper representation of the instance that will allow us
    # to deserialize it later
    serialized.should.contain('"__class__": "datetime.datetime"')
    serialized.should.contain(
        '"__value__": "2013-03-30T12:12:12.123456+03:00"')

    # When I try to deserialize, Then I see it works again
    ejson.loads(serialized).should.equal(obj)
Пример #30
0
def deserialize(msg_raw):
    msg = ejson.loads(msg_raw)
    msg_type = msg.get("msg", False)
    if not msg_type:
        raise Exception("No message type specified")
    if msg_type not in msg_types:
        raise Exception("Invalid message type")
    obj = msg_types[msg_type]
    return obj(*[msg.get(arg) for arg in obj._serialize_args])
Пример #31
0
 def test_escape_decode(self):
     self.assertEqual({
                          "foo": "bar",
                          "bat": {
                              "$escape": {
                                  "baz": "bazam"
                              }
                          }
                      }, ejson.loads('{"bat": {"$escape": {"$escape": {"baz": "bazam"}}}, "foo": "bar"}'))
Пример #32
0
    def on_message(self, message):
        """Process a message received from remote."""
        if self.ws.closed:
            return None
        try:
            self.logger.debug('< %s %r', self, message)

            # parse message set
            try:
                msgs = ejson.loads(message)
            except ValueError, err:
                self.error(400, 'Data is not valid EJSON')
                return
            if not isinstance(msgs, list):
                self.error(400, 'Invalid EJSON messages')
                return

            # process individual messages
            while msgs:
                # parse message payload
                raw = msgs.pop(0)
                try:
                    data = ejson.loads(raw)
                except ValueError, err:
                    self.error(400, 'Data is not valid EJSON')
                    continue
                if not isinstance(data, dict):
                    self.error(400, 'Invalid EJSON message payload', raw)
                    continue
                try:
                    msg = data.pop('msg')
                except KeyError:
                    self.error(
                        400, 'Bad request', offendingMessage=data,
                    )
                    continue
                # dispatch message
                try:
                    self.dispatch(msg, data)
                except MeteorError, err:
                    self.error(err)
Пример #33
0
def test_deserialization():
    ejson.cleanup_deserialization_registry()

    @ejson.register_deserializer(Person)
    def deserialize_person(data):
        return Person(data['name'], data['age'])

    obj = ejson.loads('{"age": 25, "name": "Lincoln"}')
    person = ejson.deserialize(Person, obj)
    person.should.be.a(Person)
    person.name.should.be.equals('Lincoln')
    person.age.should.be.equals(25)
Пример #34
0
def test_auto_deserialization():
    ejson.cleanup_deserialization_registry()

    @ejson.register_deserializer(Person)
    def deserialize_person(data):
        return Person(data['name'], data['age'])

    person = ejson.loads(
        '{"__class__": "tests.unit.test_ejson.Person", "__value__": {"age": 25, "name": "Lincoln"}}')
    person.should.be.a(Person)
    person.name.should.be.equals('Lincoln')
    person.age.should.be.equals(25)
Пример #35
0
def test_deserialization():
    ejson.cleanup_deserialization_registry()

    @ejson.register_deserializer(Person)
    def deserialize_person(data):
        return Person(data['name'], data['age'])

    obj = ejson.loads('{"age": 25, "name": "Lincoln"}')
    person = ejson.deserialize(Person, obj)
    person.should.be.a(Person)
    person.name.should.be.equals('Lincoln')
    person.age.should.be.equals(25)
Пример #36
0
    def recvloop(self):
        while self.websocket.open:
            while len(self.callcache) > 0:
                self.call(*self.callcache.pop(0))
            
            msg = yield from self.websocket.recv()
            if not msg: continue
            msg = ejson.loads(msg)

            if msg.get('msg') == 'connected':
                self.connected = True
                self.session = msg['session']
                
            elif msg.get('msg') == 'ping':
                yield from self.websocket.send(ejson.dumps({'msg': 'pong', 'id': msg.get('id')}))
                
            elif msg.get('msg') == 'ready':
                for sub in self.subs.values():
                    if sub.id in msg['subs']:
                        yield from sub.ready_cb(sub)

            elif msg.get('msg') == 'added':
                sub = self.subs.get(msg['collection'])
                if sub:
                    sub.data[msg['id']] = msg['fields']
                    yield from sub.added_cb(sub, msg['id'], msg['fields'])
                    
            elif msg.get('msg') == 'changed':
                sub = self.subs.get(msg['collection'])
                if sub:
                    if msg.get('fields'):
                        sub.data[msg['id']].update(msg['fields'])
                        yield from sub.changed_cb(sub, msg['id'], msg['fields'])
                    elif msg.get('cleared'):
                        for key in msg['cleared']:
                            del sub.data[key]
                        yield from sub.changed_cb(sub, msg['id'], msg['cleared'])
                    
            elif msg.get('msg') == 'removed':
                sub = self.subs.get(msg['collection'])
                if sub:
                    del sub.data[msg['id']]
                    yield from sub.removed_cb(sub, msg['id'])
                    
            elif msg.get('msg') == 'result':
                c = self.calls.get(msg['id'])
                if c:
                    yield from c.onfinished(msg.get('result'), msg.get('error'))
                    
        self.connected = False
        while True:
            yield from self.connect()
            return
Пример #37
0
 async def _websocket_message_handler(self) -> None:
     async for message in self:
         recv = ejson.loads(message)
         if recv["msg"] == "result":
             self._invoke_method_handler(recv)
         elif recv["msg"] == "ready":
             self._subscription_ready_handler(recv)
         elif recv["msg"] == "added" or recv["msg"] == "changed":
             self._subscription_message_handler(recv)
         else:
             logger.error(f"Unhandled message from server:\n%s",
                          pprint.pformat(recv))
Пример #38
0
def test_auto_deserialization():
    ejson.cleanup_deserialization_registry()

    @ejson.register_deserializer(Person)
    def deserialize_person(data):
        return Person(data['name'], data['age'])

    person = ejson.loads(
        '{"__class__": "tests.unit.test_ejson.Person", "__value__": {"age": 25, "name": "Lincoln"}}'
    )
    person.should.be.a(Person)
    person.name.should.be.equals('Lincoln')
    person.age.should.be.equals(25)
Пример #39
0
    async def __handler__(self):
        '''Handles messages received from the server'''

        while self._websocket.open:
            try:
                msg = await self._websocket.recv()
            except websockets.exceptions.ConnectionClosed:
                break

            if not msg: continue

            msg = ejson.loads(msg)
            _type = msg.get('msg')

            if _type == 'ping':
                await self.__pong__(msg.get('id'))

            elif _type == 'ready':
                for _id in msg['subs']:
                    sub = self._subs.get(_id)
                    if sub: sub.__ready__()

            elif _type == 'nosub':
                sub = self._subs.get(msg['id'])
                if sub:
                    sub.__error__(
                        msg.get('error',
                                'Denied by server for unspecified reason.'))

            elif _type == 'added':
                col = self.get_collection(msg['collection'])
                col.__added__(msg['id'], msg['fields'])

            elif _type == 'changed':
                col = self.get_collection(msg['collection'])
                col.__changed__(msg['id'], msg.get('fields', {}),
                                msg.get('cleared', []))

            elif _type == 'removed':
                col = self.get_collection(msg['collection'])
                col.__removed__(msg['id'])

            elif _type == 'result':
                c = self._calls.get(msg['id'])
                if c:
                    await c.__result__(msg.get('error'), msg.get('result'))
                    del self._calls[msg['id']]

        self.is_connected = False
        self._disconnection_event.set()
Пример #40
0
 async def invoke_method(self, method: str, params: List[Any] = []) -> Any:
     async with self._method_lock:
         id = str(uuid.uuid4())
         await super().send(
             ejson.dumps({
                 "id": id,
                 "msg": "method",
                 "method": method,
                 "params": params,
             }))
         recv = ejson.loads(await super().recv())
         if recv["id"] != id or recv["msg"] != "result":
             raise websockets.exceptions.ProtocolError("Unexpected message")
         return recv["result"]
Пример #41
0
def _assert_serialize_deserialize_equals_and_is_binary(
    ejson_obj, json_obj, json_str, is_binary
):
    assert json_str == ejson.stringify(ejson_obj)
    assert json_str == ejson.dumps(ejson_obj)
    assert json_obj == ejson.to_json_value(ejson_obj)

    assert ejson_obj == ejson.parse(json_str)
    assert ejson_obj == ejson.loads(json_str)
    assert ejson_obj == ejson.from_json_value(json_obj)

    ejson_obj2 = deepcopy(ejson_obj)

    assert ejson.equals(ejson_obj, ejson_obj2)
    assert ejson.equals(ejson_obj, ejson.clone(ejson_obj))
    assert is_binary == ejson.is_binary(ejson_obj)
Пример #42
0
 def sub_unique_objects(self, obj, params=None, pub=None, *args, **kwargs):
     """Return objects that are only visible through given subscription."""
     if params is None:
         params = ejson.loads(obj.params_ejson)
     if pub is None:
         pub = self.get_pub_by_name(obj.publication)
     queries = collections.OrderedDict(
         (col, qs) for (qs, col) in (
             self.qs_and_collection(qs)
             for qs
             in pub.get_queries(*params)
         )
     )
     # mergebox via MVCC!  For details on how this is possible, read this:
     # https://devcenter.heroku.com/articles/postgresql-concurrency
     to_send = collections.OrderedDict(
         (
             col,
             col.objects_for_user(
                 user=obj.user_id,
                 qs=qs,
                 *args, **kwargs
             ),
         )
         for col, qs
         in queries.items()
     )
     for other in Subscription.objects.filter(
             connection=obj.connection_id,
             collections__collection_name__in=[col.name for col in queries],
     ).exclude(
         pk=obj.pk,
     ).order_by('pk').distinct():
         other_pub = self.get_pub_by_name(other.publication)
         for qs in other_pub.get_queries(*other.params):
             qs, col = self.qs_and_collection(qs)
             if col not in to_send:
                 continue
             to_send[col] = to_send[col].exclude(
                 pk__in=col.objects_for_user(
                     user=other.user_id,
                     qs=qs,
                     *args, **kwargs
                 ).values('pk'),
             )
     for col, qs in to_send.items():
         yield col, qs.distinct()
Пример #43
0
    def update_subs(new_user_id):
        """Update subs to send added/removed for collections with user_rel."""
        for sub in Subscription.objects.filter(connection=this.ws.connection):
            params = loads(sub.params_ejson)
            pub = API.get_pub_by_name(sub.publication)

            # calculate the querysets prior to update
            pre = collections.OrderedDict([
                (col, query) for col, query
                in API.sub_unique_objects(sub, params, pub)
            ])

            # save the subscription with the updated user_id
            sub.user_id = new_user_id
            sub.save()

            # calculate the querysets after the update
            post = collections.OrderedDict([
                (col, query) for col, query
                in API.sub_unique_objects(sub, params, pub)
            ])

            # first pass, send `added` for objs unique to `post`
            for col_post, query in post.items():
                try:
                    qs_pre = pre[col_post]
                    query = query.exclude(
                        pk__in=qs_pre.order_by().values('pk'),
                    )
                except KeyError:
                    # collection not included pre-auth, everything is added.
                    pass
                for obj in query:
                    this.ws.send(col_post.obj_change_as_msg(obj, ADDED))

            # second pass, send `removed` for objs unique to `pre`
            for col_pre, query in pre.items():
                try:
                    qs_post = post[col_pre]
                    query = query.exclude(
                        pk__in=qs_post.order_by().values('pk'),
                    )
                except KeyError:
                    # collection not included post-auth, everything is removed.
                    pass
                for obj in query:
                    this.ws.send(col_pre.obj_change_as_msg(obj, REMOVED))
Пример #44
0
 def validated_user(cls, token, purpose, minutes_valid):
     """Resolve and validate auth token, returns user object."""
     try:
         username, auth_hash = loads(token.decode('base64'))
     except (ValueError, Error):
         cls.auth_failed(token=token)
     try:
         user = cls.user_model.objects.get(**{
             cls.user_model.USERNAME_FIELD: username,
             'is_active': True,
         })
         user.backend = 'django.contrib.auth.backends.ModelBackend'
     except cls.user_model.DoesNotExist:
         cls.auth_failed(username=username, token=token)
     if auth_hash not in iter_auth_hashes(user, purpose, minutes_valid):
         cls.auth_failed(username=username, token=token)
     return user
Пример #45
0
 def validated_user(cls, token, purpose, days_valid):
     """Resolve and validate auth token, returns user object."""
     try:
         username, auth_hash = loads(token.decode('base64'))
     except (ValueError, Error):
         cls.auth_failed(token=token)
     try:
         user = cls.user_model.objects.get(**{
             cls.user_model.USERNAME_FIELD: username,
             'is_active': True,
         })
         user.backend = 'django.contrib.auth.backends.ModelBackend'
     except cls.user_model.DoesNotExist:
         cls.auth_failed(username=username, token=token)
     if auth_hash not in iter_auth_hashes(user, purpose, days_valid):
         cls.auth_failed(username=username, token=token)
     return user
Пример #46
0
def listen_for_events():
    """Pubsub event listener

    Listen for events in the pubsub bus and calls the process function
    when somebody comes to play.
    """
    import_event_modules()
    conn = redis_connection.get_connection()
    pubsub = conn.pubsub()
    pubsub.subscribe("eventlib")
    for message in pubsub.listen():
        if message['type'] != 'message':
            continue
        data = loads(message["data"])
        if 'name' in data:
            event_name = data.pop('name')
            process_external(event_name, data)
Пример #47
0
 def validated_user_and_session(cls, token, purpose):
     """Resolve and validate auth token, returns user and session objects."""
     try:
         username, session_key, auth_hash = loads(token.decode('base64'))
     except (ValueError, Error):
         cls.auth_failed(token=token)
     try:
         user = cls.user_model.objects.get(**{
             cls.user_model.USERNAME_FIELD: username,
         })
         user.backend = 'django.contrib.auth.backends.ModelBackend'
     except cls.user_model.DoesNotExist:
         cls.auth_failed(username=username, token=token)
     if cls.get_auth_hash(user, purpose) != auth_hash:
         cls.auth_failed(username=username, token=token)
     session = SessionStore(
         session_key=session_key,
     )
     if session.get_expiry_date() <= timezone.now():
         cls.auth_failed(username=username, token=token)
     return (user, session)
Пример #48
0
def read_json(path):
    """Read JSON encoded contents from specified path."""
    with open(path, mode='r') as json_file:
        return loads(json_file.read())
Пример #49
0
 def recv(self):
     """Receive a message."""
     raw = self.websocket.recv()
     return ejson.loads(raw)
Пример #50
0
def test_forwarding_kwargs_to_loads():
    my_float = lambda f: float(f) * 2
    ejson.loads('{"num": 2.5}', parse_float=my_float).should.equal(
        {'num': 5})
Пример #51
0
def test_deserialization_with_no_objects_registered():
    ejson.cleanup_deserialization_registry()
    obj = ejson.loads('{"age": 25, "name": "Lincoln"}')
    ejson.deserialize.when.called_with(Person, obj).should.throw(
        TypeError,
        "There is no deserializer registered to handle instances of 'Person'")
    def received_message(self, data):
        """Incomming messages"""
        data = ejson.loads(str(data))
        if not data.get('msg'):
            return

        elif data['msg'] == 'failed':
            self._ddp_version_index += 1
            self._retry_new_version = data.get('version', True)
            self.emit('failed', data)

        elif data['msg'] == 'connected':
            self._session = data.get('session')
            if self._is_reconnecting:
                self.ddpsocket._debug_log("* RECONNECTED")
                self.emit('reconnected')
                self._is_reconnecting = False
            else:
                self.ddpsocket._debug_log("* CONNECTED")
                self.emit('connected')
                self._retry_new_version = False

        # method result
        elif data['msg'] == 'result':
            # call the optional callback
            callback = self._callbacks.get(data['id'])
            if callback:
                callback(data.get('error'), data.get('result'))
                self._callbacks.pop(data['id'])

        # missing subscription
        elif data['msg'] == 'nosub':
            callback = self._callbacks.get(data['id'])
            if callback:
                callback(data.get('error'), data['id'])
                self._callbacks.pop(data['id'])

        # document added to collection
        elif data['msg'] == 'added':
            self.emit('added', data['collection'],
                      data['id'], data.get('fields', {}))

        # document changed in collection
        elif data['msg'] == 'changed':
            self.emit('changed', data['collection'], data['id'],
                       data.get('fields', {}), data.get('cleared', {}))

        # document removed from collection
        elif data['msg'] == 'removed':
            self.emit('removed', data['collection'], data['id'])

        # subcription ready
        elif data['msg'] == 'ready':
            for sub_id in data.get('subs', []):
                callback = self._callbacks.get(sub_id)
                if callback:
                    callback(data.get('error'), sub_id)
                    self._callbacks.pop(sub_id)

        elif data['msg'] == 'ping':
            msg = {'msg': 'pong'}
            id = data.get('id')
            if id is not None:
                msg['id'] = id
            self.ddpsocket.send(msg)

        else:
            pass
Пример #53
0
 def test_unknown_type_decode(self):
     with self.assertRaises(ejson.UnknownTypeError):
         ejson.loads('{"foo": {"$type": "pow", "$value": 5}}')
Пример #54
0
    def __init__(self, **kwargs):
        """
        Initialisation for Django DDP server view.

        `Meteor.settings` is sourced from the following (later take precedence):
          1. django.conf.settings.METEOR_SETTINGS
          2. os.environ['METEOR_SETTINGS']
          3. MeteorView.meteor_settings (class attribute) or empty dict
          4. MeteorView.as_view(meteor_settings=...)

        Additionally, `Meteor.settings.public` is updated with values from
        environemnt variables specified from the following sources:
          1. django.conf.settings.METEOR_PUBLIC_ENVS
          2. os.environ['METEOR_PUBLIC_ENVS']
          3. MeteorView.meteor_public_envs (class attribute) or empty dict
          4. MeteorView.as_view(meteor_public_envs=...)
        """
        self.runtime_config = {}
        self.meteor_settings = reduce(
            dict_merge,
            [
                getattr(settings, 'METEOR_SETTINGS', {}),
                loads(os.environ.get('METEOR_SETTINGS', '{}')),
                self.meteor_settings or {},
                kwargs.pop('meteor_settings', {}),
            ],
            {},
        )
        self.meteor_public_envs = set()
        self.meteor_public_envs.update(
            getattr(settings, 'METEOR_PUBLIC_ENVS', []),
            os.environ.get('METEOR_PUBLIC_ENVS', '').replace(',', ' ').split(),
            self.meteor_public_envs or [],
            kwargs.pop('meteor_public_envs', []),
        )
        public = self.meteor_settings.setdefault('public', {})
        for env_name in self.meteor_public_envs:
            try:
                public[env_name] = os.environ[env_name]
            except KeyError:
                pass  # environment variable not set
        # super(...).__init__ assigns kwargs to instance.
        super(MeteorView, self).__init__(**kwargs)

        # read and process /etc/mime.types
        mimetypes.init()

        self.url_map = {}

        # process `star_json`
        self.star_json = read_json(self.json_path)
        star_format = self.star_json['format']
        if star_format != 'site-archive-pre1':
            raise ValueError(
                'Unknown Meteor star format: %r' % star_format,
            )
        programs = {
            program['name']: program
            for program in self.star_json['programs']
        }

        # process `bundle/programs/server/program.json` from build dir
        server_json_path = os.path.join(
            os.path.dirname(self.json_path),
            os.path.dirname(programs['server']['path']),
            'program.json',
        )
        server_json = read_json(server_json_path)
        server_format = server_json['format']
        if server_format != 'javascript-image-pre1':
            raise ValueError(
                'Unknown Meteor server format: %r' % server_format,
            )
        self.server_load_map = {}
        for item in server_json['load']:
            item['path_full'] = os.path.join(
                os.path.dirname(server_json_path),
                item['path'],
            )
            self.server_load_map[item['path']] = item
            self.url_map[item['path']] = (
                item['path_full'], 'text/javascript'
            )
            try:
                item['source_map_full'] = os.path.join(
                    os.path.dirname(server_json_path),
                    item['sourceMap'],
                )
                self.url_map[item['sourceMap']] = (
                    item['source_map_full'], 'text/plain'
                )
            except KeyError:
                pass
        self.template_path = os.path.join(
            os.path.dirname(server_json_path),
            self.server_load_map[
                'packages/boilerplate-generator.js'
            ][
                'assets'
            ][
                'boilerplate_web.browser.html'
            ],
        )

        # process `bundle/programs/web.browser/program.json` from build dir
        web_browser_json_path = os.path.join(
            os.path.dirname(self.json_path),
            programs['web.browser']['path'],
        )
        web_browser_json = read_json(web_browser_json_path)
        web_browser_format = web_browser_json['format']
        if web_browser_format != 'web-program-pre1':
            raise ValueError(
                'Unknown Meteor web.browser format: %r' % (
                    web_browser_format,
                ),
            )
        self.client_map = {}
        self.internal_map = {}
        for item in web_browser_json['manifest']:
            item['path_full'] = os.path.join(
                os.path.dirname(web_browser_json_path),
                item['path'],
            )
            if item['where'] == 'client':
                if '?' in item['url']:
                    item['url'] = item['url'].split('?', 1)[0]
                if item['url'].startswith('/'):
                    item['url'] = item['url'][1:]
                self.client_map[item['url']] = item
                self.url_map[item['url']] = (
                    item['path_full'],
                    mimetypes.guess_type(
                        item['path_full'],
                    )[0] or 'application/octet-stream',
                )
            elif item['where'] == 'internal':
                self.internal_map[item['type']] = item

        config = {
            'css': [
                {'url': item['path']}
                for item in web_browser_json['manifest']
                if item['type'] == 'css' and item['where'] == 'client'
            ],
            'js': [
                {'url': item['path']}
                for item in web_browser_json['manifest']
                if item['type'] == 'js' and item['where'] == 'client'
            ],
            'meteorRuntimeConfig': '"%s"' % (
                dumps(self.runtime_config)
            ),
            'rootUrlPathPrefix': self.root_url_path_prefix,
            'bundledJsCssPrefix': self.bundled_js_css_prefix,
            'inlineScriptsAllowed': False,
            'inline': None,
            'head': read(
                self.internal_map.get('head', {}).get('path_full', None),
                default=u'',
            ),
            'body': read(
                self.internal_map.get('body', {}).get('path_full', None),
                default=u'',
            ),
        }
        tmpl_raw = read(self.template_path, encoding='utf8')
        compiler = pybars.Compiler()
        tmpl = compiler.compile(tmpl_raw)
        self.html = '<!DOCTYPE html>\n%s' % tmpl(config)