예제 #1
0
    def test_outstanding_invoke(self):
        """
        When a call is pending and the callee goes away, it cancels the
        in-flight call
        """
        raise unittest.SkipTest('FIXME: Adjust unit test mocks #1567')

        session = mock.Mock()
        session._realm = u'realm1'
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({u'allow': True, u'disclose': True})
        )
        rap = RouterApplicationSession(session, self.router_factory)

        rap.send(message.Hello(u"realm1", {u'caller': role.RoleCallerFeatures()}))
        rap.send(message.Register(1, u'foo'))

        # we can retrieve the Registration via
        # session.mock_calls[-1][1][0] if req'd

        # re-set the authorize, as the Deferred from above is already
        # used-up and it gets called again to authorize the Call
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({u'allow': True, u'disclose': True})
        )
        rap.send(message.Call(42, u'foo'))

        orig = rap.send
        d = defer.Deferred()

        rap.send(message.Goodbye())

        def wrapper(*args, **kw):
            d.callback(args[0])
            return orig(*args, **kw)
        rap.send = wrapper

        # we can do this *after* the call to send() the Goodbye
        # (above) because it takes a reactor-turn to actually
        # process the cancel/errors etc -- hence the Deferred and
        # yield in this test...

        msg = yield d

        self.assertEqual(42, msg.request)
        self.assertEqual(u'wamp.error.canceled', msg.error)
        def test_on_leave_valid_session_deferred(self):
            '''
            cover when onLeave called after we have a valid session
            '''
            session = MockApplicationSession()
            exception = RuntimeError("such challenge")
            session.onLeave = async_exception_raiser(exception)
            # we have to get to an established connection first...
            session.onMessage(create_mock_welcome())
            self.assertTrue(session._session_id is not None)

            # okay we have a session ("because ._session_id is not None")
            msg = message.Goodbye()
            session.onMessage(msg)

            self.assertEqual(1, len(session.errors))
            self.assertEqual(exception, session.errors[0][0])
예제 #3
0
    def test_router_session_goodbye_custom_message(self):
        """
        Reason should be propagated properly from Goodbye message
        """
        from crossbar.router.session import RouterApplicationSession
        session = mock.Mock()
        session._realm = u'realm'
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        rap.send(message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        session.reset_mock()
        rap.send(message.Goodbye(u'wamp.reason.logout', u'some custom message'))

        leaves = [call for call in session.mock_calls if call[0] == 'onLeave']
        self.assertEqual(1, len(leaves))
        details = leaves[0][1][0]
        self.assertEqual(u'wamp.reason.logout', details.reason)
        self.assertEqual(u'some custom message', details.message)
예제 #4
0
def generate_test_messages():
   return [
      message.Hello(u"realm1", [role.RoleBrokerFeatures()]),
      message.Goodbye(),
      message.Heartbeat(123, 456),
      message.Yield(123456),
      message.Yield(123456, args = [1, 2, 3], kwargs = {u'foo': 23, u'bar': u'hello'}),
      message.Yield(123456, progress = True),
      message.Interrupt(123456),
      message.Interrupt(123456, mode = message.Interrupt.KILL),
      message.Invocation(123456, 789123),
      message.Invocation(123456, 789123, args = [1, 2, 3], kwargs = {u'foo': 23, u'bar': u'hello'}),
      message.Invocation(123456, 789123, timeout = 10000),
      message.Result(123456),
      message.Result(123456, args = [1, 2, 3], kwargs = {u'foo': 23, u'bar': u'hello'}),
      message.Result(123456, progress = True),
      message.Cancel(123456),
      message.Cancel(123456, mode = message.Cancel.KILL),
      message.Call(123456, u'com.myapp.procedure1'),
      message.Call(123456, u'com.myapp.procedure1', args = [1, 2, 3], kwargs = {u'foo': 23, u'bar': u'hello'}),
      message.Call(123456, u'com.myapp.procedure1', timeout = 10000),
      message.Unregistered(123456),
      message.Unregister(123456, 789123),
      message.Registered(123456, 789123),
      message.Register(123456, u'com.myapp.procedure1'),
      message.Register(123456, u'com.myapp.procedure1', pkeys = [10, 11, 12]),
      message.Event(123456, 789123),
      message.Event(123456, 789123, args = [1, 2, 3], kwargs = {u'foo': 23, u'bar': u'hello'}),
      message.Event(123456, 789123, publisher = 300),
      message.Published(123456, 789123),
      message.Publish(123456, u'com.myapp.topic1'),
      message.Publish(123456, u'com.myapp.topic1', args = [1, 2, 3], kwargs = {u'foo': 23, u'bar': u'hello'}),
      message.Publish(123456, u'com.myapp.topic1', excludeMe = False, exclude = [300], eligible = [100, 200, 300], discloseMe = True),
      message.Unsubscribed(123456),
      message.Unsubscribe(123456, 789123),
      message.Subscribed(123456, 789123),
      message.Subscribe(123456, u'com.myapp.topic1'),
      message.Subscribe(123456, u'com.myapp.topic1', match = message.Subscribe.MATCH_PREFIX),
      message.Error(message.Call.MESSAGE_TYPE, 123456, u'com.myapp.error1'),
      message.Error(message.Call.MESSAGE_TYPE, 123456, u'com.myapp.error1', args = [1, 2, 3], kwargs = {u'foo': 23, u'bar': u'hello'}),
      message.Call(123456, u'com.myapp.\u4f60\u597d\u4e16\u754c', args=[1, 2, 3]),
      message.Result(123456, args=[1, 2, 3], kwargs={u'en': u'Hello World', u'jp': u'\u3053\u3093\u306b\u3061\u306f\u4e16\u754c'})
   ]
예제 #5
0
    def test_onleave_publish(self):
        """
        Receiving a Goodbye should put the session in a "detached" state
        *before* the onLeave method is triggered
        """

        router = mock.MagicMock()
        router.new_correlation_id = lambda: 'fake correlation id'
        utest = self

        class TestSession(RouterSession):
            def __init__(self, *args, **kw):
                super(TestSession, self).__init__(*args, **kw)
                # for this test, pretend we're connected (without
                # going through sending a Hello etc.)
                self._service_session = mock.MagicMock()
                self._transport = mock.MagicMock()
                self._session_id = 1234
                self._router = router  # normally done in Hello processing
                self.on_leave_called = False  # so the test ensures onLeave called

            def onLeave(self, reason):
                utest.assertFalse(self.on_leave_called)
                # we check that when this method is called, we look
                # like we're un-joined
                utest.assertIs(None, self._session_id)
                utest.assertTrue(self._goodbye_sent)
                # on the router, .detach() should have been called
                utest.assertEqual(1, len(router.method_calls))
                utest.assertEqual('detach', router.method_calls[0][0])
                self.on_leave_called = True

        router_factory = mock.MagicMock()
        session = TestSession(router_factory)
        goodbye = message.Goodbye('wamp.close.normal', 'hi there')

        self.assertFalse(session._goodbye_sent)

        # do the test; we check results in onLeave
        self.assertFalse(session.on_leave_called)
        session.onMessage(goodbye)
        self.assertTrue(session.on_leave_called)
예제 #6
0
    def test_router_session_goodbye_onLeave_error(self):
        """
        Reason should be propagated properly from Goodbye message
        """
        from crossbar.router.session import RouterApplicationSession
        session = mock.Mock()
        the_exception = RuntimeError("onLeave fails")

        def boom(*args, **kw):
            raise the_exception
        session.onLeave = mock.Mock(side_effect=boom)
        session._realm = u'realm'
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        rap.send(message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        session.reset_mock()
        rap.send(message.Goodbye(u'wamp.reason.logout', u'some custom message'))

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors))
        self.assertEqual(the_exception, errors[0].value)
예제 #7
0
    def test_wamp_session_on_leave(self):
        """
        wamp.session.on_leave receives valid session_id
        """

        router = mock.MagicMock()
        router.new_correlation_id = lambda: 'fake correlation id'

        class TestSession(RouterSession):
            def __init__(self, *args, **kw):
                super(TestSession, self).__init__(*args, **kw)
                # for this test, pretend we're connected (without
                # going through sending a Hello etc.)
                self._transport = mock.MagicMock()
                self._transport.get_channel_id = mock.MagicMock(
                    return_value=b'deadbeef')
                self._session_id = 1234
                self._router = router  # normally done in Hello processing
                self._service_session = mock.MagicMock()

        router_factory = mock.MagicMock()
        session = TestSession(router_factory)
        goodbye = message.Goodbye('wamp.close.normal', 'hi there')

        self.assertFalse(session._goodbye_sent)

        # do the test; we should publish wamp.session.on_leave via the
        # service-session
        session.onMessage(goodbye)
        publishes = [
            call for call in session._service_session.mock_calls
            if call[0] == 'publish'
        ]
        self.assertEqual(1, len(publishes))
        call = publishes[0]
        self.assertEqual(call[0], "publish")
        self.assertEqual(call[1], ("wamp.session.on_leave", 1234))
예제 #8
0
    def test_router_session_goodbye_custom_message(self):
        """
        Reason should be propagated properly from Goodbye message
        """

        from crossbar.router.session import RouterApplicationSession
        session = ApplicationSession()
        session.onLeave = mock.Mock()
        session._realm = 'realm'
        router = Router(factory=mock.Mock(),
                        realm=RouterRealm(
                            controller=None,
                            id='realm',
                            config=dict(name='realm'),
                        ))
        rap = RouterApplicationSession(session, router)

        rap.send(message.Goodbye('wamp.reason.logout', 'some custom message'))

        leaves = session.onLeave.mock_calls
        self.assertEqual(1, len(leaves))
        details = leaves[0][1][0]
        self.assertEqual('wamp.reason.logout', details.reason)
        self.assertEqual('some custom message', details.message)
예제 #9
0
    def test_router_session_goodbye_fire_disconnect_error(self):
        """
        Reason should be propagated properly from Goodbye message
        """
        from crossbar.router.session import RouterApplicationSession
        session = mock.Mock()
        the_exception = RuntimeError("sad times at ridgemont high")

        def boom(*args, **kw):
            if args[0] == 'disconnect':
                return defer.fail(the_exception)
            return defer.succeed(None)
        session.fire = mock.Mock(side_effect=boom)
        session._realm = u'realm'
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        rap.send(message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        session.reset_mock()
        rap.send(message.Goodbye(u'wamp.reason.logout', u'some custom message'))

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors))
        self.assertEqual(the_exception, errors[0].value)
예제 #10
0
def generate_test_messages():
    """
    List of WAMP test message used for serializers. Expand this if you add more
    options or messages.

    This list of WAMP message does not contain any binary app payloads!
    """
    some_bytes = os.urandom(32)
    some_unicode = '\u3053\u3093\u306b\u3061\u306f\u4e16\u754c'

    some_uri = 'com.myapp.foobar'
    some_unicode_uri = 'com.myapp.\u4f60\u597d\u4e16\u754c.baz'

    some_args = [1, 2, 3, 'hello', some_bytes, some_unicode, {'foo': 23, 'bar': 'hello', 'baz': some_bytes, 'moo': some_unicode}]
    some_kwargs = {'foo': 23, 'bar': 'hello', 'baz': some_bytes, 'moo': some_unicode, 'arr': some_args}

    msgs = [
        message.Hello("realm1", {'subscriber': role.RoleSubscriberFeatures()}),
        message.Hello("realm1", {'publisher': role.RolePublisherFeatures()}),
        message.Hello("realm1", {'caller': role.RoleCallerFeatures()}),
        message.Hello("realm1", {'callee': role.RoleCalleeFeatures()}),
        message.Hello("realm1", {
            'subscriber': role.RoleSubscriberFeatures(),
            'publisher': role.RolePublisherFeatures(),
            'caller': role.RoleCallerFeatures(),
            'callee': role.RoleCalleeFeatures(),
        }),
        message.Goodbye(),
        message.Yield(123456),
        message.Yield(123456, args=some_args),
        message.Yield(123456, args=[], kwargs=some_kwargs),
        message.Yield(123456, args=some_args, kwargs=some_kwargs),
        message.Yield(123456, progress=True),
        message.Interrupt(123456),
        message.Interrupt(123456, mode=message.Interrupt.KILL),
        message.Invocation(123456, 789123),
        message.Invocation(123456, 789123, args=some_args),
        message.Invocation(123456, 789123, args=[], kwargs=some_kwargs),
        message.Invocation(123456, 789123, args=some_args, kwargs=some_kwargs),
        message.Invocation(123456, 789123, timeout=10000),
        message.Result(123456),
        message.Result(123456, args=some_args),
        message.Result(123456, args=[], kwargs=some_kwargs),
        message.Result(123456, args=some_args, kwargs=some_kwargs),
        message.Result(123456, progress=True),
        message.Cancel(123456),
        message.Cancel(123456, mode=message.Cancel.KILL),
        message.Call(123456, some_uri),
        message.Call(123456, some_uri, args=some_args),
        message.Call(123456, some_uri, args=[], kwargs=some_kwargs),
        message.Call(123456, some_uri, args=some_args, kwargs=some_kwargs),
        message.Call(123456, some_uri, timeout=10000),
        message.Call(123456, some_unicode_uri),
        message.Call(123456, some_unicode_uri, args=some_args),
        message.Call(123456, some_unicode_uri, args=[], kwargs=some_kwargs),
        message.Call(123456, some_unicode_uri, args=some_args, kwargs=some_kwargs),
        message.Call(123456, some_unicode_uri, timeout=10000),
        message.Unregistered(123456),
        message.Unregister(123456, 789123),
        message.Registered(123456, 789123),
        message.Register(123456, some_uri),
        message.Register(123456, some_uri, match='prefix'),
        message.Register(123456, some_uri, invoke='roundrobin'),
        message.Register(123456, some_unicode_uri),
        message.Register(123456, some_unicode_uri, match='prefix'),
        message.Register(123456, some_unicode_uri, invoke='roundrobin'),
        message.Event(123456, 789123),
        message.Event(123456, 789123, args=some_args),
        message.Event(123456, 789123, args=[], kwargs=some_kwargs),
        message.Event(123456, 789123, args=some_args, kwargs=some_kwargs),
        message.Event(123456, 789123, publisher=300),
        message.Published(123456, 789123),
        message.Publish(123456, some_uri),
        message.Publish(123456, some_uri, args=some_args),
        message.Publish(123456, some_uri, args=[], kwargs=some_kwargs),
        message.Publish(123456, some_uri, args=some_args, kwargs=some_kwargs),
        message.Publish(123456, some_uri, exclude_me=False, exclude=[300], eligible=[100, 200, 300]),
        message.Publish(123456, some_unicode_uri),
        message.Publish(123456, some_unicode_uri, args=some_args),
        message.Publish(123456, some_unicode_uri, args=[], kwargs=some_kwargs),
        message.Publish(123456, some_unicode_uri, args=some_args, kwargs=some_kwargs),
        message.Publish(123456, some_unicode_uri, exclude_me=False, exclude=[300], eligible=[100, 200, 300]),
        message.Unsubscribed(123456),
        message.Unsubscribe(123456, 789123),
        message.Subscribed(123456, 789123),
        message.Subscribe(123456, some_uri),
        message.Subscribe(123456, some_uri, match=message.Subscribe.MATCH_PREFIX),
        message.Subscribe(123456, some_unicode_uri),
        message.Subscribe(123456, some_unicode_uri, match=message.Subscribe.MATCH_PREFIX),
        message.Error(message.Call.MESSAGE_TYPE, 123456, some_uri),
        message.Error(message.Call.MESSAGE_TYPE, 123456, some_uri, args=some_args),
        message.Error(message.Call.MESSAGE_TYPE, 123456, some_uri, args=[], kwargs=some_kwargs),
        message.Error(message.Call.MESSAGE_TYPE, 123456, some_uri, args=some_args, kwargs=some_kwargs),
        message.Error(message.Call.MESSAGE_TYPE, 123456, some_unicode_uri),
        message.Error(message.Call.MESSAGE_TYPE, 123456, some_unicode_uri, args=some_args),
        message.Error(message.Call.MESSAGE_TYPE, 123456, some_unicode_uri, args=[], kwargs=some_kwargs),
        message.Error(message.Call.MESSAGE_TYPE, 123456, some_unicode_uri, args=some_args, kwargs=some_kwargs),
        message.Result(123456),
        message.Result(123456, args=some_args),
        message.Result(123456, args=some_args, kwargs=some_kwargs),
    ]
    return [(False, msg) for msg in msgs]
예제 #11
0
    def onMessage(self, msg):
        """
      Implements :func:`autobahn.wamp.interfaces.ITransportHandler.onMessage`
      """
        if self._session_id is None:

            if not self._pending_session_id:
                self._pending_session_id = util.id()

            def welcome(realm,
                        authid=None,
                        authrole=None,
                        authmethod=None,
                        authprovider=None):
                self._session_id = self._pending_session_id
                self._pending_session_id = None
                self._goodbye_sent = False

                self._router = self._router_factory.get(realm)
                if not self._router:
                    raise Exception("no such realm")

                self._authid = authid
                self._authrole = authrole
                self._authmethod = authmethod
                self._authprovider = authprovider

                roles = self._router.attach(self)

                msg = message.Welcome(self._session_id,
                                      roles,
                                      authid=authid,
                                      authrole=authrole,
                                      authmethod=authmethod,
                                      authprovider=authprovider)
                self._transport.send(msg)

                self.onJoin(
                    SessionDetails(self._realm, self._session_id, self._authid,
                                   self._authrole, self._authmethod,
                                   self._authprovider))

            ## the first message MUST be HELLO
            if isinstance(msg, message.Hello):

                self._realm = msg.realm

                details = types.HelloDetails(msg.roles, msg.authmethods,
                                             msg.authid,
                                             self._pending_session_id)

                d = self._as_future(self.onHello, self._realm, details)

                def success(res):
                    msg = None

                    if isinstance(res, types.Accept):
                        welcome(self._realm, res.authid, res.authrole,
                                res.authmethod, res.authprovider)

                    elif isinstance(res, types.Challenge):
                        msg = message.Challenge(res.method, res.extra)

                    elif isinstance(res, types.Deny):
                        msg = message.Abort(res.reason, res.message)

                    else:
                        pass

                    if msg:
                        self._transport.send(msg)

                def failed(err):
                    print(err.value)

                self._add_future_callbacks(d, success, failed)

            elif isinstance(msg, message.Authenticate):

                d = self._as_future(self.onAuthenticate, msg.signature,
                                    msg.extra)

                def success(res):
                    msg = None

                    if isinstance(res, types.Accept):
                        welcome(self._realm, res.authid, res.authrole,
                                res.authmethod, res.authprovider)

                    elif isinstance(res, types.Deny):
                        msg = message.Abort(res.reason, res.message)

                    else:
                        pass

                    if msg:
                        self._transport.send(msg)

                def failed(err):
                    print(err.value)

                self._add_future_callbacks(d, success, failed)

            elif isinstance(msg, message.Abort):

                ## fire callback and close the transport
                self.onLeave(types.CloseDetails(msg.reason, msg.message))

                self._session_id = None
                self._pending_session_id = None

                #self._transport.close()

            else:
                raise ProtocolError(
                    "Received {0} message, and session is not yet established".
                    format(msg.__class__))

        else:

            if isinstance(msg, message.Hello):
                raise ProtocolError(
                    u"HELLO message received, while session is already established"
                )

            elif isinstance(msg, message.Goodbye):
                if not self._goodbye_sent:
                    ## the peer wants to close: send GOODBYE reply
                    reply = message.Goodbye()
                    self._transport.send(reply)

                ## fire callback and close the transport
                self.onLeave(types.CloseDetails(msg.reason, msg.message))

                self._router.detach(self)

                self._session_id = None
                self._pending_session_id = None

                #self._transport.close()

            elif isinstance(msg, message.Heartbeat):

                pass  ## FIXME

            else:

                self._router.process(self, msg)
예제 #12
0
 def connectionLost(self, reason):
     if self._wamp_session:
         msg = message.Goodbye()
         self._wamp_session.onMessage(msg)
         del self._wamp_session
예제 #13
0
    def onMessage(self, msg):
        """
        Implements :func:`autobahn.wamp.interfaces.ITransportHandler.onMessage`
        """
        if self._session_id is None:

            if not self._pending_session_id:
                self._pending_session_id = util.id()

            def welcome(realm,
                        authid=None,
                        authrole=None,
                        authmethod=None,
                        authprovider=None):
                self._session_id = self._pending_session_id
                self._pending_session_id = None
                self._goodbye_sent = False

                self._router = self._router_factory.get(realm)
                if not self._router:
                    raise Exception("no such realm")

                self._authid = authid
                self._authrole = authrole
                self._authmethod = authmethod
                self._authprovider = authprovider

                roles = self._router.attach(self)

                msg = message.Welcome(self._session_id,
                                      roles,
                                      authid=authid,
                                      authrole=authrole,
                                      authmethod=authmethod,
                                      authprovider=authprovider)
                self._transport.send(msg)

                self.onJoin(
                    SessionDetails(self._realm, self._session_id, self._authid,
                                   self._authrole, self._authmethod,
                                   self._authprovider))

            # the first message MUST be HELLO
            if isinstance(msg, message.Hello):

                self._realm = msg.realm
                self._session_roles = msg.roles

                details = types.HelloDetails(msg.roles, msg.authmethods,
                                             msg.authid,
                                             self._pending_session_id)

                d = txaio.as_future(self.onHello, self._realm, details)

                def success(res):
                    msg = None

                    if isinstance(res, types.Accept):
                        welcome(self._realm, res.authid, res.authrole,
                                res.authmethod, res.authprovider)

                    elif isinstance(res, types.Challenge):
                        msg = message.Challenge(res.method, res.extra)

                    elif isinstance(res, types.Deny):
                        msg = message.Abort(res.reason, res.message)

                    else:
                        pass

                    if msg:
                        self._transport.send(msg)

                txaio.add_callbacks(d, success, self._swallow_error_and_abort)

            elif isinstance(msg, message.Authenticate):

                d = txaio.as_future(self.onAuthenticate, msg.signature, {})

                def success(res):
                    msg = None

                    if isinstance(res, types.Accept):
                        welcome(self._realm, res.authid, res.authrole,
                                res.authmethod, res.authprovider)

                    elif isinstance(res, types.Deny):
                        msg = message.Abort(res.reason, res.message)

                    else:
                        pass

                    if msg:
                        self._transport.send(msg)

                txaio.add_callbacks(d, success, self._swallow_error_and_abort)

            elif isinstance(msg, message.Abort):

                # fire callback and close the transport
                self.onLeave(types.CloseDetails(msg.reason, msg.message))

                self._session_id = None
                self._pending_session_id = None

                # self._transport.close()

            else:
                raise ProtocolError(
                    "Received {0} message, and session is not yet established".
                    format(msg.__class__))

        else:

            if isinstance(msg, message.Hello):
                raise ProtocolError(
                    u"HELLO message received, while session is already established"
                )

            elif isinstance(msg, message.Goodbye):
                if not self._goodbye_sent:
                    # The peer wants to close: answer with GOODBYE reply.
                    # Note: We MUST NOT send any WAMP message _after_ GOODBYE
                    reply = message.Goodbye()
                    self._transport.send(reply)
                    self._goodbye_sent = True
                else:
                    # This is the peer's GOODBYE reply to our own earlier GOODBYE
                    pass

                # We need to first detach the session from the router before
                # erasing the session ID below ..
                self._router.detach(self)

                # At this point, we've either sent GOODBYE already earlier,
                # or we have just responded with GOODBYE. In any case, we MUST NOT
                # send any WAMP message from now on:
                # clear out session ID, so that anything that might be triggered
                # in the onLeave below is prohibited from sending WAMP stuff.
                # E.g. the client might have been subscribed to meta events like
                # wamp.session.on_leave - and we must not send that client's own
                # leave to itself!
                self._session_id = None
                self._pending_session_id = None

                # fire callback and close the transport
                self.onLeave(types.CloseDetails(msg.reason, msg.message))

                # don't close the transport, as WAMP allows to reattach a session
                # to the same or a different realm without closing the transport
                # self._transport.close()

            else:

                self._router.process(self, msg)
예제 #14
0
   def onMessage(self, msg):
      """
      Implements :func:`autobahn.wamp.interfaces.ITransportHandler.onMessage`
      """
      if self._session_id is None:

         ## the first message must be WELCOME, ABORT or CHALLENGE ..
         ##
         if isinstance(msg, message.Welcome):
            self._session_id = msg.session

            details = SessionDetails(self._realm, self._session_id, msg.authid, msg.authrole, msg.authmethod)
            self._as_future(self.onJoin, details)

         elif isinstance(msg, message.Abort):

            ## fire callback and close the transport
            self.onLeave(types.CloseDetails(msg.reason, msg.message))

         elif isinstance(msg, message.Challenge):

            challenge = types.Challenge(msg.method, msg.extra)
            d = self._as_future(self.onChallenge, challenge)

            def success(signature):
               reply = message.Authenticate(signature)
               self._transport.send(reply)

            def error(err):
               reply = message.Abort(u"wamp.error.cannot_authenticate", u"{0}".format(err.value))
               self._transport.send(reply)
               ## fire callback and close the transport
               self.onLeave(types.CloseDetails(reply.reason, reply.message))

            self._add_future_callbacks(d, success, error)

         else:
            raise ProtocolError("Received {0} message, and session is not yet established".format(msg.__class__))

      else:

         if isinstance(msg, message.Goodbye):
            if not self._goodbye_sent:
               ## the peer wants to close: send GOODBYE reply
               reply = message.Goodbye()
               self._transport.send(reply)

            self._session_id = None

            ## fire callback and close the transport
            self.onLeave(types.CloseDetails(msg.reason, msg.message))

         ## consumer messages
         ##
         elif isinstance(msg, message.Event):

            if msg.subscription in self._subscriptions:

               handler = self._subscriptions[msg.subscription]

               if handler.details_arg:
                  if not msg.kwargs:
                     msg.kwargs = {}
                  msg.kwargs[handler.details_arg] = types.EventDetails(publication = msg.publication, publisher = msg.publisher)

               try:
                  if handler.obj:
                     if msg.kwargs:
                        if msg.args:
                           handler.fn(handler.obj, *msg.args, **msg.kwargs)
                        else:
                           handler.fn(handler.obj, **msg.kwargs)
                     else:
                        if msg.args:
                           handler.fn(handler.obj, *msg.args)
                        else:
                           handler.fn(handler.obj)
                  else:
                     if msg.kwargs:
                        if msg.args:
                           handler.fn(*msg.args, **msg.kwargs)
                        else:
                           handler.fn(**msg.kwargs)
                     else:
                        if msg.args:
                           handler.fn(*msg.args)
                        else:
                           handler.fn()

               except Exception as e:
                  if self.debug_app:
                     print("Failure while firing event handler {0} subscribed under '{1}' ({2}): {3}".format(handler.fn, handler.topic, msg.subscription, e))

            else:
               raise ProtocolError("EVENT received for non-subscribed subscription ID {0}".format(msg.subscription))

         elif isinstance(msg, message.Published):

            if msg.request in self._publish_reqs:
               d, opts = self._publish_reqs.pop(msg.request)
               p = Publication(msg.publication)
               self._resolve_future(d, p)
            else:
               raise ProtocolError("PUBLISHED received for non-pending request ID {0}".format(msg.request))

         elif isinstance(msg, message.Subscribed):

            if msg.request in self._subscribe_reqs:
               d, obj, fn, topic, options = self._subscribe_reqs.pop(msg.request)
               if options:
                  self._subscriptions[msg.subscription] = Handler(obj, fn, topic, options.details_arg)
               else:
                  self._subscriptions[msg.subscription] = Handler(obj, fn, topic)
               s = Subscription(self, msg.subscription)
               self._resolve_future(d, s)
            else:
               raise ProtocolError("SUBSCRIBED received for non-pending request ID {0}".format(msg.request))

         elif isinstance(msg, message.Unsubscribed):

            if msg.request in self._unsubscribe_reqs:
               d, subscription = self._unsubscribe_reqs.pop(msg.request)
               if subscription.id in self._subscriptions:
                  del self._subscriptions[subscription.id]
               subscription.active = False
               self._resolve_future(d, None)
            else:
               raise ProtocolError("UNSUBSCRIBED received for non-pending request ID {0}".format(msg.request))

         elif isinstance(msg, message.Result):

            if msg.request in self._call_reqs:

               if msg.progress:

                  ## progressive result
                  ##
                  _, opts = self._call_reqs[msg.request]
                  if opts.onProgress:
                     try:
                        if msg.kwargs:
                           if msg.args:
                              opts.onProgress(*msg.args, **msg.kwargs)
                           else:
                              opts.onProgress(**msg.kwargs)
                        else:
                           if msg.args:
                              opts.onProgress(*msg.args)
                           else:
                              opts.onProgress()
                     except Exception as e:
                        ## silently drop exceptions raised in progressive results handlers
                        if self.debug:
                           print("Exception raised in progressive results handler: {0}".format(e))
                  else:
                     ## silently ignore progressive results
                     pass
               else:

                  ## final result
                  ##
                  d, opts = self._call_reqs.pop(msg.request)
                  if msg.kwargs:
                     if msg.args:
                        res = types.CallResult(*msg.args, **msg.kwargs)
                     else:
                        res = types.CallResult(**msg.kwargs)
                     self._resolve_future(d, res)
                  else:
                     if msg.args:
                        if len(msg.args) > 1:
                           res = types.CallResult(*msg.args)
                           self._resolve_future(d, res)
                        else:
                           self._resolve_future(d, msg.args[0])
                     else:
                        self._resolve_future(d, None)
            else:
               raise ProtocolError("RESULT received for non-pending request ID {0}".format(msg.request))

         elif isinstance(msg, message.Invocation):

            if msg.request in self._invocations:

               raise ProtocolError("INVOCATION received for request ID {0} already invoked".format(msg.request))

            else:

               if msg.registration not in self._registrations:

                  raise ProtocolError("INVOCATION received for non-registered registration ID {0}".format(msg.registration))

               else:
                  endpoint = self._registrations[msg.registration]

                  if endpoint.options and endpoint.options.details_arg:

                     if not msg.kwargs:
                        msg.kwargs = {}

                     if msg.receive_progress:
                        def progress(*args, **kwargs):
                           progress_msg = message.Yield(msg.request, args = args, kwargs = kwargs, progress = True)
                           self._transport.send(progress_msg)
                     else:
                        progress = None

                     msg.kwargs[endpoint.options.details_arg] = types.CallDetails(progress, caller = msg.caller,
                        caller_transport = msg.caller_transport, authid = msg.authid, authrole = msg.authrole,
                        authmethod = msg.authmethod)

                  if endpoint.obj:
                     if msg.kwargs:
                        if msg.args:
                           d = self._as_future(endpoint.fn, endpoint.obj, *msg.args, **msg.kwargs)
                        else:
                           d = self._as_future(endpoint.fn, endpoint.obj, **msg.kwargs)
                     else:
                        if msg.args:
                           d = self._as_future(endpoint.fn, endpoint.obj, *msg.args)
                        else:
                           d = self._as_future(endpoint.fn, endpoint.obj)
                  else:
                     if msg.kwargs:
                        if msg.args:
                           d = self._as_future(endpoint.fn, *msg.args, **msg.kwargs)
                        else:
                           d = self._as_future(endpoint.fn, **msg.kwargs)
                     else:
                        if msg.args:
                           d = self._as_future(endpoint.fn, *msg.args)
                        else:
                           d = self._as_future(endpoint.fn)

                  def success(res):
                     del self._invocations[msg.request]

                     if isinstance(res, types.CallResult):
                        reply = message.Yield(msg.request, args = res.results, kwargs = res.kwresults)
                     else:
                        reply = message.Yield(msg.request, args = [res])
                     self._transport.send(reply)

                  def error(err):
                     if self.traceback_app:
                        ## if asked to marshal the traceback within the WAMP error message, extract it
                        # noinspection PyCallingNonCallable
                        tb = StringIO()
                        err.printTraceback(file = tb)
                        tb = tb.getvalue().splitlines()
                     else:
                        tb = None

                     if self.debug_app:
                        print("Failure while invoking procedure {0} registered under '{1}' ({2}):".format(endpoint.fn, endpoint.procedure, msg.registration))
                        print(err)

                     del self._invocations[msg.request]

                     if hasattr(err, 'value'):
                        exc = err.value
                     else:
                        exc = err
                     reply = self._message_from_exception(message.Invocation.MESSAGE_TYPE, msg.request, exc, tb)
                     self._transport.send(reply)

                  self._invocations[msg.request] = d

                  self._add_future_callbacks(d, success, error)

         elif isinstance(msg, message.Interrupt):

            if msg.request not in self._invocations:
               raise ProtocolError("INTERRUPT received for non-pending invocation {0}".format(msg.request))
            else:
               # noinspection PyBroadException
               try:
                  self._invocations[msg.request].cancel()
               except Exception:
                  if self.debug:
                     print("could not cancel call {0}".format(msg.request))
               finally:
                  del self._invocations[msg.request]

         elif isinstance(msg, message.Registered):

            if msg.request in self._register_reqs:
               d, obj, fn, procedure, options = self._register_reqs.pop(msg.request)
               self._registrations[msg.registration] = Endpoint(obj, fn, procedure, options)
               r = Registration(self, msg.registration)
               self._resolve_future(d, r)
            else:
               raise ProtocolError("REGISTERED received for non-pending request ID {0}".format(msg.request))

         elif isinstance(msg, message.Unregistered):

            if msg.request in self._unregister_reqs:
               d, registration = self._unregister_reqs.pop(msg.request)
               if registration.id in self._registrations:
                  del self._registrations[registration.id]
               registration.active = False
               self._resolve_future(d, None)
            else:
               raise ProtocolError("UNREGISTERED received for non-pending request ID {0}".format(msg.request))

         elif isinstance(msg, message.Error):

            d = None

            ## ERROR reply to PUBLISH
            ##
            if msg.request_type == message.Publish.MESSAGE_TYPE and msg.request in self._publish_reqs:
               d = self._publish_reqs.pop(msg.request)[0]

            ## ERROR reply to SUBSCRIBE
            ##
            elif msg.request_type == message.Subscribe.MESSAGE_TYPE and msg.request in self._subscribe_reqs:
               d = self._subscribe_reqs.pop(msg.request)[0]

            ## ERROR reply to UNSUBSCRIBE
            ##
            elif msg.request_type == message.Unsubscribe.MESSAGE_TYPE and msg.request in self._unsubscribe_reqs:
               d = self._unsubscribe_reqs.pop(msg.request)[0]

            ## ERROR reply to REGISTER
            ##
            elif msg.request_type == message.Register.MESSAGE_TYPE and msg.request in self._register_reqs:
               d = self._register_reqs.pop(msg.request)[0]

            ## ERROR reply to UNREGISTER
            ##
            elif msg.request_type == message.Unregister.MESSAGE_TYPE and msg.request in self._unregister_reqs:
               d = self._unregister_reqs.pop(msg.request)[0]

            ## ERROR reply to CALL
            ##
            elif msg.request_type == message.Call.MESSAGE_TYPE and msg.request in self._call_reqs:
               d = self._call_reqs.pop(msg.request)[0]

            if d:
               self._reject_future(d, self._exception_from_message(msg))
            else:
               raise ProtocolError("WampAppSession.onMessage(): ERROR received for non-pending request_type {0} and request ID {1}".format(msg.request_type, msg.request))

         elif isinstance(msg, message.Heartbeat):

            pass ## FIXME

         else:

            raise ProtocolError("Unexpected message {0}".format(msg.__class__))
예제 #15
0
 def test_str(self):
    e = message.Goodbye(reason = u'wamp.error.system_shutdown', message = u'The host is shutting down now.')
    self.assertIsInstance(str(e), str)
예제 #16
0
def generate_test_messages():
    """
    List of WAMP test message used for serializers. Expand this if you add more
    options or messages.

    This list of WAMP message does not contain any binary app payloads!
    """
    msgs = [
        message.Hello(u"realm1",
                      {u'subscriber': role.RoleSubscriberFeatures()}),
        message.Goodbye(),
        message.Yield(123456),
        message.Yield(123456,
                      args=[1, 2, 3],
                      kwargs={
                          u'foo': 23,
                          u'bar': u'hello'
                      }),
        message.Yield(123456, args=[u'hello']),
        message.Yield(123456, progress=True),
        message.Interrupt(123456),
        message.Interrupt(123456, mode=message.Interrupt.KILL),
        message.Invocation(123456, 789123),
        message.Invocation(123456,
                           789123,
                           args=[1, 2, 3],
                           kwargs={
                               u'foo': 23,
                               u'bar': u'hello'
                           }),
        message.Invocation(123456, 789123, timeout=10000),
        message.Result(123456),
        message.Result(123456,
                       args=[1, 2, 3],
                       kwargs={
                           u'foo': 23,
                           u'bar': u'hello'
                       }),
        message.Result(123456, progress=True),
        message.Cancel(123456),
        message.Cancel(123456, mode=message.Cancel.KILL),
        message.Call(123456, u'com.myapp.procedure1'),
        message.Call(123456,
                     u'com.myapp.procedure1',
                     args=[1, 2, 3],
                     kwargs={
                         u'foo': 23,
                         u'bar': u'hello'
                     }),
        message.Call(123456, u'com.myapp.procedure1', timeout=10000),
        message.Unregistered(123456),
        message.Unregister(123456, 789123),
        message.Registered(123456, 789123),
        message.Register(123456, u'com.myapp.procedure1'),
        message.Register(123456, u'com.myapp.procedure1', match=u'prefix'),
        message.Register(123456, u'com.myapp.procedure1',
                         invoke=u'roundrobin'),
        message.Event(123456, 789123),
        message.Event(123456,
                      789123,
                      args=[1, 2, 3],
                      kwargs={
                          u'foo': 23,
                          u'bar': u'hello'
                      }),
        message.Event(123456, 789123, publisher=300),
        message.Published(123456, 789123),
        message.Publish(123456, u'com.myapp.topic1'),
        message.Publish(123456,
                        u'com.myapp.topic1',
                        args=[1, 2, 3],
                        kwargs={
                            u'foo': 23,
                            u'bar': u'hello'
                        }),
        message.Publish(123456,
                        u'com.myapp.topic1',
                        exclude_me=False,
                        exclude=[300],
                        eligible=[100, 200, 300]),
        message.Unsubscribed(123456),
        message.Unsubscribe(123456, 789123),
        message.Subscribed(123456, 789123),
        message.Subscribe(123456, u'com.myapp.topic1'),
        message.Subscribe(123456,
                          u'com.myapp.topic1',
                          match=message.Subscribe.MATCH_PREFIX),
        message.Error(message.Call.MESSAGE_TYPE, 123456, u'com.myapp.error1'),
        message.Error(message.Call.MESSAGE_TYPE,
                      123456,
                      u'com.myapp.error1',
                      args=[1, 2, 3],
                      kwargs={
                          u'foo': 23,
                          u'bar': u'hello'
                      }),
        message.Call(123456,
                     u'com.myapp.\u4f60\u597d\u4e16\u754c',
                     args=[1, 2, 3]),
        message.Result(123456,
                       args=[1, 2, 3],
                       kwargs={
                           u'en': u'Hello World',
                           u'jp': u'\u3053\u3093\u306b\u3061\u306f\u4e16\u754c'
                       })
    ]
    return [(False, msg) for msg in msgs]
예제 #17
0
    def onMessage(self, msg):
        """
        Implements :func:`autobahn.wamp.interfaces.ITransportHandler.onMessage`
        """
        if self._session_id is None:

            if not self._pending_session_id:
                self._pending_session_id = util.id()

            def welcome(realm,
                        authid=None,
                        authrole=None,
                        authmethod=None,
                        authprovider=None,
                        authextra=None,
                        custom=None):
                self._realm = realm
                self._session_id = self._pending_session_id
                self._pending_session_id = None
                self._goodbye_sent = False

                self._router = self._router_factory.get(realm)
                if not self._router:
                    # should not arrive here
                    raise Exception(
                        "logic error (no realm at a stage were we should have one)"
                    )

                self._authid = authid
                self._authrole = authrole
                self._authmethod = authmethod
                self._authprovider = authprovider

                roles = self._router.attach(self)

                msg = message.Welcome(self._session_id,
                                      roles,
                                      realm=realm,
                                      authid=authid,
                                      authrole=authrole,
                                      authmethod=authmethod,
                                      authprovider=authprovider,
                                      authextra=authextra,
                                      custom=custom)
                self._transport.send(msg)

                self.onJoin(
                    SessionDetails(self._realm, self._session_id, self._authid,
                                   self._authrole, self._authmethod,
                                   self._authprovider, self._authextra))

            # the first message MUST be HELLO
            if isinstance(msg, message.Hello):

                self._session_roles = msg.roles

                details = types.HelloDetails(
                    realm=msg.realm,
                    authmethods=msg.authmethods,
                    authid=msg.authid,
                    authrole=msg.authrole,
                    authextra=msg.authextra,
                    session_roles=msg.roles,
                    pending_session=self._pending_session_id)

                d = txaio.as_future(self.onHello, msg.realm, details)

                def success(res):
                    msg = None
                    if isinstance(res, types.Accept):
                        custom = {
                            # FIXME:
                            # u'x_cb_node_id': self._router_factory._node_id
                            u'x_cb_node_id': None
                        }
                        welcome(res.realm, res.authid, res.authrole,
                                res.authmethod, res.authprovider,
                                res.authextra, custom)

                    elif isinstance(res, types.Challenge):
                        msg = message.Challenge(res.method, res.extra)

                    elif isinstance(res, types.Deny):
                        msg = message.Abort(res.reason, res.message)

                    else:
                        pass

                    if msg:
                        self._transport.send(msg)

                txaio.add_callbacks(d, success, self._swallow_error_and_abort)

            elif isinstance(msg, message.Authenticate):

                d = txaio.as_future(self.onAuthenticate, msg.signature, {})

                def success(res):
                    msg = None
                    if isinstance(res, types.Accept):
                        custom = {
                            # FIXME:
                            # u'x_cb_node_id': self._router_factory._node_id
                            u'x_cb_node_id': None
                        }
                        welcome(res.realm, res.authid, res.authrole,
                                res.authmethod, res.authprovider,
                                res.authextra, custom)

                    elif isinstance(res, types.Deny):
                        msg = message.Abort(res.reason, res.message)

                    else:
                        pass

                    if msg:
                        self._transport.send(msg)

                txaio.add_callbacks(d, success, self._swallow_error_and_abort)

            elif isinstance(msg, message.Abort):

                # fire callback and close the transport
                self.onLeave(types.CloseDetails(msg.reason, msg.message))

                self._session_id = None
                self._pending_session_id = None

                # self._transport.close()

            else:
                # raise ProtocolError(u"PReceived {0} message while session is not joined".format(msg.__class__))
                # self.log.warn('Protocol state error - received {message} while session is not joined')
                # swallow all noise like still getting PUBLISH messages from log event forwarding - maybe FIXME
                pass

        else:

            if isinstance(msg, message.Hello):
                raise ProtocolError(
                    u"HELLO message received, while session is already established"
                )

            elif isinstance(msg, message.Goodbye):
                if not self._goodbye_sent:
                    # The peer wants to close: answer with GOODBYE reply.
                    # Note: We MUST NOT send any WAMP message _after_ GOODBYE
                    reply = message.Goodbye()
                    self._transport.send(reply)
                    self._goodbye_sent = True
                else:
                    # This is the peer's GOODBYE reply to our own earlier GOODBYE
                    pass

                # We need to first detach the session from the router before
                # erasing the session ID below ..
                try:
                    self._router.detach(self)
                except Exception:
                    self.log.failure("Internal error")

                # In order to send wamp.session.on_leave properly
                # (i.e. *with* the proper session_id) we save it
                previous_session_id = self._session_id

                # At this point, we've either sent GOODBYE already earlier,
                # or we have just responded with GOODBYE. In any case, we MUST NOT
                # send any WAMP message from now on:
                # clear out session ID, so that anything that might be triggered
                # in the onLeave below is prohibited from sending WAMP stuff.
                # E.g. the client might have been subscribed to meta events like
                # wamp.session.on_leave - and we must not send that client's own
                # leave to itself!
                self._session_id = None
                self._pending_session_id = None

                # publish event, *after* self._session_id is None so
                # that we don't publish to ourselves as well (if this
                # session happens to be subscribed to wamp.session.on_leave)
                if self._service_session:
                    self._service_session.publish(
                        u'wamp.session.on_leave',
                        previous_session_id,
                    )

                # fire callback and close the transport
                self.onLeave(types.CloseDetails(msg.reason, msg.message))

                # don't close the transport, as WAMP allows to reattach a session
                # to the same or a different realm without closing the transport
                # self._transport.close()

            else:
                self._router.process(self, msg)
예제 #18
0
    def onMessage(self, msg):
        """
        Implements :func:`autobahn.wamp.interfaces.ITransportHandler.onMessage`
        """
        if self._session_id is None:

            # the first message must be WELCOME, ABORT or CHALLENGE ..
            if isinstance(msg, message.Welcome):
                self._session_id = msg.session

                details = SessionDetails(self._realm, self._session_id,
                                         msg.authid, msg.authrole,
                                         msg.authmethod)
                d = txaio.as_future(self.onJoin, details)

                def _error(e):
                    return self._swallow_error(e, "While firing onJoin")

                txaio.add_callbacks(d, None, _error)

            elif isinstance(msg, message.Abort):

                # fire callback and close the transport
                details = types.CloseDetails(msg.reason, msg.message)
                d = txaio.as_future(self.onLeave, details)

                def _error(e):
                    return self._swallow_error(e, "While firing onLeave")

                txaio.add_callbacks(d, None, _error)

            elif isinstance(msg, message.Challenge):

                challenge = types.Challenge(msg.method, msg.extra)
                d = txaio.as_future(self.onChallenge, challenge)

                def success(signature):
                    if not isinstance(signature, six.text_type):
                        raise Exception('signature must be unicode')
                    reply = message.Authenticate(signature)
                    self._transport.send(reply)

                def error(err):
                    self.onUserError(err, "Authentication failed")
                    reply = message.Abort(u"wamp.error.cannot_authenticate",
                                          u"{0}".format(err.value))
                    self._transport.send(reply)
                    # fire callback and close the transport
                    details = types.CloseDetails(reply.reason, reply.message)
                    d = txaio.as_future(self.onLeave, details)

                    def _error(e):
                        return self._swallow_error(e, "While firing onLeave")

                    txaio.add_callbacks(d, None, _error)
                    # switching to the callback chain, effectively
                    # cancelling error (which we've now handled)
                    return d

                txaio.add_callbacks(d, success, error)

            else:
                raise ProtocolError(
                    "Received {0} message, and session is not yet established".
                    format(msg.__class__))

        else:
            # self._session_id != None (aka "session established")
            if isinstance(msg, message.Goodbye):
                if not self._goodbye_sent:
                    # the peer wants to close: send GOODBYE reply
                    reply = message.Goodbye()
                    self._transport.send(reply)

                self._session_id = None

                # fire callback and close the transport
                details = types.CloseDetails(msg.reason, msg.message)
                d = txaio.as_future(self.onLeave, details)

                def _error(e):
                    errmsg = 'While firing onLeave for reason "{0}" and message "{1}"'.format(
                        msg.reason, msg.message)
                    return self._swallow_error(e, errmsg)

                txaio.add_callbacks(d, None, _error)

            elif isinstance(msg, message.Event):

                if msg.subscription in self._subscriptions:

                    # fire all event handlers on subscription ..
                    for subscription in self._subscriptions[msg.subscription]:

                        handler = subscription.handler

                        invoke_args = (
                            handler.obj, ) if handler.obj else tuple()
                        if msg.args:
                            invoke_args = invoke_args + tuple(msg.args)

                        invoke_kwargs = msg.kwargs if msg.kwargs else dict()
                        if handler.details_arg:
                            invoke_kwargs[
                                handler.details_arg] = types.EventDetails(
                                    publication=msg.publication,
                                    publisher=msg.publisher,
                                    topic=msg.topic or subscription.topic)

                        def _error(e):
                            errmsg = 'While firing {0} subscribed under {1}.'.format(
                                handler.fn, msg.subscription)
                            return self._swallow_error(e, errmsg)

                        future = txaio.as_future(handler.fn, *invoke_args,
                                                 **invoke_kwargs)
                        txaio.add_callbacks(future, None, _error)

                else:
                    raise ProtocolError(
                        "EVENT received for non-subscribed subscription ID {0}"
                        .format(msg.subscription))

            elif isinstance(msg, message.Published):

                if msg.request in self._publish_reqs:

                    # get and pop outstanding publish request
                    publish_request = self._publish_reqs.pop(msg.request)

                    # create a new publication object
                    publication = Publication(msg.publication)

                    # resolve deferred/future for publishing successfully
                    txaio.resolve(publish_request.on_reply, publication)
                else:
                    raise ProtocolError(
                        "PUBLISHED received for non-pending request ID {0}".
                        format(msg.request))

            elif isinstance(msg, message.Subscribed):

                if msg.request in self._subscribe_reqs:

                    # get and pop outstanding subscribe request
                    request = self._subscribe_reqs.pop(msg.request)

                    # create new handler subscription list for subscription ID if not yet tracked
                    if msg.subscription not in self._subscriptions:
                        self._subscriptions[msg.subscription] = []

                    subscription = Subscription(msg.subscription,
                                                request.topic, self,
                                                request.handler)

                    # add handler to existing subscription
                    self._subscriptions[msg.subscription].append(subscription)

                    # resolve deferred/future for subscribing successfully
                    txaio.resolve(request.on_reply, subscription)
                else:
                    raise ProtocolError(
                        "SUBSCRIBED received for non-pending request ID {0}".
                        format(msg.request))

            elif isinstance(msg, message.Unsubscribed):

                if msg.request in self._unsubscribe_reqs:

                    # get and pop outstanding subscribe request
                    request = self._unsubscribe_reqs.pop(msg.request)

                    # if the subscription still exists, mark as inactive and remove ..
                    if request.subscription_id in self._subscriptions:
                        for subscription in self._subscriptions[
                                request.subscription_id]:
                            subscription.active = False
                        del self._subscriptions[request.subscription_id]

                    # resolve deferred/future for unsubscribing successfully
                    txaio.resolve(request.on_reply, 0)
                else:
                    raise ProtocolError(
                        "UNSUBSCRIBED received for non-pending request ID {0}".
                        format(msg.request))

            elif isinstance(msg, message.Result):

                if msg.request in self._call_reqs:

                    if msg.progress:

                        # progressive result
                        call_request = self._call_reqs[msg.request]
                        if call_request.options.on_progress:
                            kw = msg.kwargs or dict()
                            args = msg.args or tuple()
                            try:
                                # XXX what if on_progress returns a Deferred/Future?
                                call_request.options.on_progress(*args, **kw)
                            except Exception:
                                try:
                                    self.onUserError(
                                        txaio.create_failure(),
                                        "While firing on_progress",
                                    )
                                except:
                                    pass

                        else:
                            # silently ignore progressive results
                            pass

                    else:
                        # final result
                        #
                        call_request = self._call_reqs.pop(msg.request)

                        on_reply = call_request.on_reply

                        if msg.kwargs:
                            if msg.args:
                                res = types.CallResult(*msg.args, **msg.kwargs)
                            else:
                                res = types.CallResult(**msg.kwargs)
                            txaio.resolve(on_reply, res)
                        else:
                            if msg.args:
                                if len(msg.args) > 1:
                                    res = types.CallResult(*msg.args)
                                    txaio.resolve(on_reply, res)
                                else:
                                    txaio.resolve(on_reply, msg.args[0])
                            else:
                                txaio.resolve(on_reply, None)
                else:
                    raise ProtocolError(
                        "RESULT received for non-pending request ID {0}".
                        format(msg.request))

            elif isinstance(msg, message.Invocation):

                if msg.request in self._invocations:

                    raise ProtocolError(
                        "INVOCATION received for request ID {0} already invoked"
                        .format(msg.request))

                else:

                    if msg.registration not in self._registrations:

                        raise ProtocolError(
                            "INVOCATION received for non-registered registration ID {0}"
                            .format(msg.registration))

                    else:
                        registration = self._registrations[msg.registration]
                        endpoint = registration.endpoint

                        if endpoint.obj is not None:
                            invoke_args = (endpoint.obj, )
                        else:
                            invoke_args = tuple()

                        if msg.args:
                            invoke_args = invoke_args + tuple(msg.args)

                        invoke_kwargs = msg.kwargs if msg.kwargs else dict()

                        if endpoint.details_arg:

                            if msg.receive_progress:

                                def progress(*args, **kwargs):
                                    progress_msg = message.Yield(msg.request,
                                                                 args=args,
                                                                 kwargs=kwargs,
                                                                 progress=True)
                                    self._transport.send(progress_msg)
                            else:
                                progress = None

                            invoke_kwargs[
                                endpoint.details_arg] = types.CallDetails(
                                    progress,
                                    caller=msg.caller,
                                    procedure=msg.procedure)

                        on_reply = txaio.as_future(endpoint.fn, *invoke_args,
                                                   **invoke_kwargs)

                        def success(res):
                            del self._invocations[msg.request]

                            if isinstance(res, types.CallResult):
                                reply = message.Yield(msg.request,
                                                      args=res.results,
                                                      kwargs=res.kwresults)
                            else:
                                reply = message.Yield(msg.request, args=[res])

                            try:
                                self._transport.send(reply)
                            except SerializationError as e:
                                # the application-level payload returned from the invoked procedure can't be serialized
                                reply = message.Error(
                                    message.Invocation.MESSAGE_TYPE,
                                    msg.request,
                                    ApplicationError.INVALID_PAYLOAD,
                                    args=[
                                        u'success return value from invoked procedure "{0}" could not be serialized: {1}'
                                        .format(registration.procedure, e)
                                    ])
                                self._transport.send(reply)

                        def error(err):
                            errmsg = txaio.failure_message(err)
                            try:
                                self.onUserError(err, errmsg)
                            except:
                                pass
                            formatted_tb = None
                            if self.traceback_app:
                                formatted_tb = txaio.failure_format_traceback(
                                    err)

                            del self._invocations[msg.request]

                            reply = self._message_from_exception(
                                message.Invocation.MESSAGE_TYPE,
                                msg.request,
                                err.value,
                                formatted_tb,
                            )

                            try:
                                self._transport.send(reply)
                            except SerializationError as e:
                                # the application-level payload returned from the invoked procedure can't be serialized
                                reply = message.Error(
                                    message.Invocation.MESSAGE_TYPE,
                                    msg.request,
                                    ApplicationError.INVALID_PAYLOAD,
                                    args=[
                                        u'error return value from invoked procedure "{0}" could not be serialized: {1}'
                                        .format(registration.procedure, e)
                                    ])
                                self._transport.send(reply)
                            # we have handled the error, so we eat it
                            return None

                        self._invocations[msg.request] = InvocationRequest(
                            msg.request, on_reply)

                        txaio.add_callbacks(on_reply, success, error)

            elif isinstance(msg, message.Interrupt):

                if msg.request not in self._invocations:
                    raise ProtocolError(
                        "INTERRUPT received for non-pending invocation {0}".
                        format(msg.request))
                else:
                    # noinspection PyBroadException
                    try:
                        self._invocations[msg.request].cancel()
                    except Exception:
                        # XXX can .cancel() return a Deferred/Future?
                        try:
                            self.onUserError(
                                txaio.create_failure(),
                                "While cancelling call.",
                            )
                        except:
                            pass
                    finally:
                        del self._invocations[msg.request]

            elif isinstance(msg, message.Registered):

                if msg.request in self._register_reqs:

                    # get and pop outstanding register request
                    request = self._register_reqs.pop(msg.request)

                    # create new registration if not yet tracked
                    if msg.registration not in self._registrations:
                        registration = Registration(self, msg.registration,
                                                    request.procedure,
                                                    request.endpoint)
                        self._registrations[msg.registration] = registration
                    else:
                        raise ProtocolError(
                            "REGISTERED received for already existing registration ID {0}"
                            .format(msg.registration))

                    txaio.resolve(request.on_reply, registration)
                else:
                    raise ProtocolError(
                        "REGISTERED received for non-pending request ID {0}".
                        format(msg.request))

            elif isinstance(msg, message.Unregistered):

                if msg.request in self._unregister_reqs:

                    # get and pop outstanding subscribe request
                    request = self._unregister_reqs.pop(msg.request)

                    # if the registration still exists, mark as inactive and remove ..
                    if request.registration_id in self._registrations:
                        self._registrations[
                            request.registration_id].active = False
                        del self._registrations[request.registration_id]

                    # resolve deferred/future for unregistering successfully
                    txaio.resolve(request.on_reply)
                else:
                    raise ProtocolError(
                        "UNREGISTERED received for non-pending request ID {0}".
                        format(msg.request))

            elif isinstance(msg, message.Error):

                # remove outstanding request and get the reply deferred/future
                on_reply = None

                # ERROR reply to CALL
                if msg.request_type == message.Call.MESSAGE_TYPE and msg.request in self._call_reqs:
                    on_reply = self._call_reqs.pop(msg.request).on_reply

                # ERROR reply to PUBLISH
                elif msg.request_type == message.Publish.MESSAGE_TYPE and msg.request in self._publish_reqs:
                    on_reply = self._publish_reqs.pop(msg.request).on_reply

                # ERROR reply to SUBSCRIBE
                elif msg.request_type == message.Subscribe.MESSAGE_TYPE and msg.request in self._subscribe_reqs:
                    on_reply = self._subscribe_reqs.pop(msg.request).on_reply

                # ERROR reply to UNSUBSCRIBE
                elif msg.request_type == message.Unsubscribe.MESSAGE_TYPE and msg.request in self._unsubscribe_reqs:
                    on_reply = self._unsubscribe_reqs.pop(msg.request).on_reply

                # ERROR reply to REGISTER
                elif msg.request_type == message.Register.MESSAGE_TYPE and msg.request in self._register_reqs:
                    on_reply = self._register_reqs.pop(msg.request).on_reply

                # ERROR reply to UNREGISTER
                elif msg.request_type == message.Unregister.MESSAGE_TYPE and msg.request in self._unregister_reqs:
                    on_reply = self._unregister_reqs.pop(msg.request).on_reply

                if on_reply:
                    txaio.reject(on_reply, self._exception_from_message(msg))
                else:
                    raise ProtocolError(
                        "WampAppSession.onMessage(): ERROR received for non-pending request_type {0} and request ID {1}"
                        .format(msg.request_type, msg.request))

            else:

                raise ProtocolError("Unexpected message {0}".format(
                    msg.__class__))