Beispiel #1
0
    def onMessage(self, msg):
        """
        Callback fired when a WAMP message was received. May run asynchronously. The callback
        should return or fire the returned deferred/future when it's done processing the message.
        In particular, an implementation of this callback must not access the message afterwards.

        :param msg: The WAMP message received.
        :type msg: object implementing :class:`autobahn.wamp.interfaces.IMessage`
        """
        if self._session_id is None:
            # no frontend session established yet, so we expect one of HELLO, ABORT, AUTHENTICATE

            # https://wamp-proto.org/_static/gen/wamp_latest.html#session-establishment
            if isinstance(msg, message.Hello):
                yield self._process_Hello(msg)

            # https://wamp-proto.org/_static/gen/wamp_latest.html#session-closing
            elif isinstance(msg, message.Abort):
                self.transport.send(message.Abort(ApplicationError.AUTHENTICATION_FAILED,
                                                  message='Client aborted the session opening handshake'))

            # https://wamp-proto.org/_static/gen/wamp_latest.html#wamp-level-authentication
            elif isinstance(msg, message.Authenticate):
                yield self._process_Authenticate(msg)

            else:
                raise ProtocolError("Received {} message while proxy frontend session is not joined".format(msg.__class__.__name__))

        else:
            # frontend session is established: process WAMP message

            if isinstance(msg, message.Hello) or isinstance(msg, message.Abort) or isinstance(msg, message.Authenticate):
                raise ProtocolError("Received {} message while proxy frontend session is already joined".format(msg.__class__.__name__))

            # https://wamp-proto.org/_static/gen/wamp_latest.html#session-closing
            elif isinstance(msg, message.Goodbye):
                if self._backend_session:
                    yield self._controller.unmap_backend(self, self._backend_session)
                    self._backend_session = None
                else:
                    self.log.warn('Frontend session left, but no active backend session to close')

                # complete the closing handshake (initiated by the client in this case) by replying with GOODBYE
                self.transport.send(message.Goodbye(message='Client aborted the session opening handshake'))
            else:
                if self._backend_session is None or self._backend_session._transport is None:
                    raise TransportLost(
                        "Expected to relay {} message, but proxy backend session or transport is gone".format(
                            msg.__class__.__name__,
                        )
                    )
                else:
                    # if we have an active backend connection, forward the WAMP message ..
                    self._backend_session._transport.send(msg)
Beispiel #2
0
   def processInvocationError(self, session, error):
      """
      Implements :func:`autobahn.wamp.interfaces.IDealer.processInvocationError`
      """
      assert(session in self._session_to_registrations)

      if error.request in self._invocations:

         ## get original call message and calling session
         ##
         call_msg, call_session = self._invocations[error.request]

         ## validate payload
         ##
         try:
            self._router.validate('call_error', call_msg.procedure, error.args, error.kwargs)
         except Exception as e:
            reply = message.Error(message.Call.MESSAGE_TYPE, call_msg.request, ApplicationError.INVALID_ARGUMENT, ["call error from procedure '{0}' with invalid application payload: {1}".format(call_msg.procedure, e)])
         else:
            reply = message.Error(message.Call.MESSAGE_TYPE, call_msg.request, error.error, args = error.args, kwargs = error.kwargs)


         ## the calling session might have been lost in the meantime ..
         ##
         if call_session._transport:
            call_session._transport.send(reply)

         ## the call is done
         ##
         del self._invocations[error.request]

      else:
         raise ProtocolError("Dealer.onInvocationError(): ERROR received for non-pending request_type {0} and request ID {1}".format(error.request_type, error.request))
Beispiel #3
0
    def processInvocationError(self, session, error):
        """
        Implements :func:`crossbar.router.interfaces.IDealer.processInvocationError`
        """
        # assert(session in self._session_to_registrations)

        if error.request in self._invocations:

            # get the invocation request tracked for the caller
            #
            invocation_request = self._invocations[error.request]

            # validate payload
            #
            try:
                self._router.validate('call_error', invocation_request.call.procedure, error.args, error.kwargs)
            except Exception as e:
                reply = message.Error(message.Call.MESSAGE_TYPE, invocation_request.call.request, ApplicationError.INVALID_ARGUMENT, ["call error from procedure '{0}' with invalid application payload: {1}".format(invocation_request.call.procedure, e)])
            else:
                reply = message.Error(message.Call.MESSAGE_TYPE, invocation_request.call.request, error.error, args=error.args, kwargs=error.kwargs)

            # the calling session might have been lost in the meantime ..
            #
            if invocation_request.caller._transport:
                invocation_request.caller._transport.send(reply)

            # the call is done
            #
            del self._invocations[error.request]

        else:
            raise ProtocolError("Dealer.onInvocationError(): ERROR received for non-pending request_type {0} and request ID {1}".format(error.request_type, error.request))
Beispiel #4
0
    def process(self, session, msg):
        """
        Implements :func:`autobahn.wamp.interfaces.IRouter.process`
        """
        if self._check_trace(session, msg):
            self.log.info(">>RX>> {msg}", msg=msg)

        try:
            # Broker
            #
            if isinstance(msg, message.Publish):
                self._broker.processPublish(session, msg)

            elif isinstance(msg, message.Subscribe):
                self._broker.processSubscribe(session, msg)

            elif isinstance(msg, message.Unsubscribe):
                self._broker.processUnsubscribe(session, msg)

            elif isinstance(msg, message.EventReceived):
                # FIXME
                self._broker.processEventReceived(session, msg)

            # Dealer
            #
            elif isinstance(msg, message.Register):
                self._dealer.processRegister(session, msg)

            elif isinstance(msg, message.Unregister):
                self._dealer.processUnregister(session, msg)

            elif isinstance(msg, message.Call):
                self._dealer.processCall(session, msg)

            elif isinstance(msg, message.Cancel):
                self._dealer.processCancel(session, msg)

            elif isinstance(msg, message.Yield):
                self._dealer.processYield(session, msg)

            elif isinstance(
                    msg, message.Error
            ) and msg.request_type == message.Invocation.MESSAGE_TYPE:
                self._dealer.processInvocationError(session, msg)

            else:
                raise ProtocolError("Unexpected message {0}".format(
                    msg.__class__))
        except ProtocolError:
            raise
        except:
            self.log.error(
                'INTERNAL ERROR in router incoming message processing')
            self.log.failure()

        # update WAMP message routing statistics
        msg_type = msg.__class__.__name__.lower()
        if msg_type not in self._message_stats['received']:
            self._message_stats['received'][msg_type] = 0
        self._message_stats['received'][msg_type] += 1
Beispiel #5
0
   def processYield(self, session, yield_):
      """
      Implements :func:`autobahn.wamp.interfaces.IDealer.processYield`
      """
      assert(session in self._session_to_registrations)

      if yield_.request in self._invocations:

         ## get original call message and calling session
         ##
         call_msg, call_session = self._invocations[yield_.request]

         ## validate payload
         ##
         is_valid = True
         try:
            self._router.validate('call_result', call_msg.procedure, yield_.args, yield_.kwargs)
         except Exception as e:
            is_valid = False
            reply = message.Error(message.Call.MESSAGE_TYPE, call_msg.request, ApplicationError.INVALID_ARGUMENT, ["call result from procedure '{0}' with invalid application payload: {1}".format(call_msg.procedure, e)])
         else:
            reply = message.Result(call_msg.request, args = yield_.args, kwargs = yield_.kwargs, progress = yield_.progress)

         ## the calling session might have been lost in the meantime ..
         ##
         if call_session._transport:
            call_session._transport.send(reply)

         ## the call is done if it's a regular call (non-progressive) or if the payload was invalid
         ##
         if not yield_.progress or not is_valid:
            del self._invocations[yield_.request]

      else:
         raise ProtocolError("Dealer.onYield(): YIELD received for non-pending request ID {0}".format(yield_.request))
Beispiel #6
0
    def processYield(self, session, yield_):
        """
        Implements :func:`crossbar.router.interfaces.IDealer.processYield`
        """
        # assert(session in self._session_to_registrations)

        if yield_.request in self._invocations:

            # get the invocation request tracked for the caller
            #
            invocation_request = self._invocations[yield_.request]

            is_valid = True
            if yield_.payload is None:
                # validate normal args/kwargs payload
                try:
                    self._router.validate('call_result', invocation_request.call.procedure, yield_.args, yield_.kwargs)
                except Exception as e:
                    is_valid = False
                    reply = message.Error(message.Call.MESSAGE_TYPE, invocation_request.call.request, ApplicationError.INVALID_ARGUMENT, [u"call result from procedure '{0}' with invalid application payload: {1}".format(invocation_request.call.procedure, e)])
                else:
                    reply = message.Result(invocation_request.call.request, args=yield_.args, kwargs=yield_.kwargs, progress=yield_.progress)
            else:
                reply = message.Result(invocation_request.call.request, payload=yield_.payload, progress=yield_.progress,
                                       enc_algo=yield_.enc_algo, enc_key=yield_.enc_key, enc_serializer=yield_.enc_serializer)

            # the calling session might have been lost in the meantime ..
            #
            if invocation_request.caller._transport:
                self._router.send(invocation_request.caller, reply)

            # the call is done if it's a regular call (non-progressive) or if the payload was invalid
            #
            if not yield_.progress or not is_valid:
                callee_extra = invocation_request.registration.observers_extra.get(session, None)
                if callee_extra:
                    callee_extra.concurrency_current -= 1

                self._remove_invoke_request(invocation_request)

                # check for any calls queued on the registration for which an
                # invocation just returned, and hence there is likely concurrency
                # free again to actually forward calls previously queued calls
                # that were queued because no callee endpoint concurrency was free
                if self._call_store:
                    queued_call = self._call_store.get_queued_call(invocation_request.registration)
                    if queued_call:
                        invocation_sent = self._call(queued_call.session,
                                                     queued_call.call,
                                                     queued_call.registration,
                                                     queued_call.authorization,
                                                     True)
                        # only actually pop the queued call when we really were
                        # able to forward the call now
                        if invocation_sent:
                            self._call_store.pop_queued_call(invocation_request.registration)

        else:
            raise ProtocolError(u"Dealer.onYield(): YIELD received for non-pending request ID {0}".format(yield_.request))
Beispiel #7
0
 def _check_all_bool(self):
     # check feature attributes
     for k in self.__dict__:
         if not k.startswith('_') and k != 'ROLE':
             if getattr(self,
                        k) is not None and type(getattr(self, k)) != bool:
                 raise ProtocolError(
                     "invalid type {0} for feature '{1}' for role '{2}'".
                     format(getattr(self, k), k, self.ROLE))
Beispiel #8
0
    def processInvocationError(self, session, error):
        """
        Implements :func:`crossbar.router.interfaces.IDealer.processInvocationError`
        """
        # assert(session in self._session_to_registrations)

        if error.request in self._invocations:

            # get the invocation request tracked for the caller
            invocation_request = self._invocations[error.request]

            # correlate the received invocation error to the original call
            error.correlation = invocation_request.call.correlation

            # if concurrency is enabled on this, an error counts as
            # "an answer" so we decrement.
            callee_extra = invocation_request.registration.observers_extra.get(session, None)
            if callee_extra:
                callee_extra.concurrency_current -= 1

            if error.payload is None:
                # validate normal args/kwargs payload
                try:
                    self._router.validate('call_error', invocation_request.call.procedure, error.args, error.kwargs)
                except Exception as e:
                    reply = message.Error(message.Call.MESSAGE_TYPE,
                                          invocation_request.call.request,
                                          ApplicationError.INVALID_ARGUMENT,
                                          [u"call error from procedure '{0}' with invalid application payload: {1}".format(invocation_request.call.procedure, e)])
                else:
                    reply = message.Error(message.Call.MESSAGE_TYPE,
                                          invocation_request.call.request,
                                          error.error,
                                          args=error.args,
                                          kwargs=error.kwargs)
            else:
                reply = message.Error(message.Call.MESSAGE_TYPE,
                                      invocation_request.call.request,
                                      error.error,
                                      payload=error.payload,
                                      enc_algo=error.enc_algo,
                                      enc_key=error.enc_key,
                                      enc_serializer=error.enc_serializer)

            # the calling session might have been lost in the meantime ..
            #
            if invocation_request.caller._transport:
                reply.correlation = invocation_request.call.correlation
                self._router.send(invocation_request.caller, reply)

            # the call is done
            #
            invoke = self._invocations[error.request]
            self._remove_invoke_request(invoke)

        else:
            raise ProtocolError(u"Dealer.onInvocationError(): ERROR received for non-pending request_type {0} and request ID {1}".format(error.request_type, error.request))
Beispiel #9
0
    def processYield(self, session, yield_):
        """
        Implements :func:`crossbar.router.interfaces.IDealer.processYield`
        """
        # assert(session in self._session_to_registrations)

        if yield_.request in self._invocations:

            # get the invocation request tracked for the caller
            #
            invocation_request = self._invocations[yield_.request]

            is_valid = True
            if yield_.payload is None:
                # validate normal args/kwargs payload
                try:
                    self._router.validate('call_result',
                                          invocation_request.call.procedure,
                                          yield_.args, yield_.kwargs)
                except Exception as e:
                    is_valid = False
                    reply = message.Error(
                        message.Call.MESSAGE_TYPE,
                        invocation_request.call.request,
                        ApplicationError.INVALID_ARGUMENT, [
                            u"call result from procedure '{0}' with invalid application payload: {1}"
                            .format(invocation_request.call.procedure, e)
                        ])
                else:
                    reply = message.Result(invocation_request.call.request,
                                           args=yield_.args,
                                           kwargs=yield_.kwargs,
                                           progress=yield_.progress)
            else:
                reply = message.Result(invocation_request.call.request,
                                       payload=yield_.payload,
                                       progress=yield_.progress,
                                       enc_algo=yield_.enc_algo,
                                       enc_key=yield_.enc_key,
                                       enc_serializer=yield_.enc_serializer)

            # the calling session might have been lost in the meantime ..
            #
            if invocation_request.caller._transport:
                self._router.send(invocation_request.caller, reply)

            # the call is done if it's a regular call (non-progressive) or if the payload was invalid
            #
            if not yield_.progress or not is_valid:
                del self._invocations[yield_.request]

        else:
            raise ProtocolError(
                u"Dealer.onYield(): YIELD received for non-pending request ID {0}"
                .format(yield_.request))
Beispiel #10
0
   def unserialize(self, payload, isBinary = None):
      """
      Implements :func:`autobahn.wamp.interfaces.ISerializer.unserialize`
      """
      if isBinary is not None:
         if isBinary != self._serializer.BINARY:
            raise ProtocolError("invalid serialization of WAMP message (binary {}, but expected {})".format(isBinary, self._serializer.BINARY))

      try:
         raw_msg = self._serializer.unserialize(payload)
      except Exception as e:
         raise ProtocolError("invalid serialization of WAMP message ({})".format(e))

      if type(raw_msg) != list:
         raise ProtocolError("invalid type {} for WAMP message".format(type(raw_msg)))

      if len(raw_msg) == 0:
         raise ProtocolError("missing message type in WAMP message")

      message_type = raw_msg[0]

      if type(message_type) != int:
         raise ProtocolError("invalid type {} for WAMP message type".format(type(message_type)))

      Klass = self.MESSAGE_TYPE_MAP.get(message_type)

      if Klass is None:
         raise ProtocolError("invalid WAMP message type {}".format(message_type))

      ## this might again raise `ProtocolError` ..
      msg = Klass.parse(raw_msg)

      return msg
Beispiel #11
0
    def process(self, session, msg):
        """
        Implements :func:`autobahn.wamp.interfaces.IRouter.process`
        """
        if self._check_trace(session, msg):
            self.log.info(">>RX>> {msg}", msg=msg)

        if self._is_traced:
            self._factory._worker._maybe_trace_rx_msg(session, msg)

        # Broker
        #
        if isinstance(msg, message.Publish):
            self._broker.processPublish(session, msg)

        elif isinstance(msg, message.Subscribe):
            self._broker.processSubscribe(session, msg)

        elif isinstance(msg, message.Unsubscribe):
            self._broker.processUnsubscribe(session, msg)

        elif isinstance(msg, message.EventReceived):
            self._broker.processEventReceived(session, msg)

        # Dealer
        #
        elif isinstance(msg, message.Register):
            self._dealer.processRegister(session, msg)

        elif isinstance(msg, message.Unregister):
            self._dealer.processUnregister(session, msg)

        elif isinstance(msg, message.Call):
            self._dealer.processCall(session, msg)

        elif isinstance(msg, message.Cancel):
            self._dealer.processCancel(session, msg)

        elif isinstance(msg, message.Yield):
            self._dealer.processYield(session, msg)

        elif isinstance(
                msg, message.Error
        ) and msg.request_type == message.Invocation.MESSAGE_TYPE:
            self._dealer.processInvocationError(session, msg)

        else:
            raise ProtocolError("Unexpected message {0}".format(msg.__class__))
Beispiel #12
0
    def unserialize(self, payload, isBinary=None):
        """
        Implements :func:`autobahn.wamp.interfaces.ISerializer.unserialize`
        """
        if isBinary is not None:
            if isBinary != self._serializer.BINARY:
                raise ProtocolError(
                    "invalid serialization of WAMP message (binary {0}, but expected {1})".format(isBinary,
                                                                                                  self._serializer.BINARY))
        try:
            raw_msgs = self._serializer.unserialize(payload)
        except Exception as e:
            raise ProtocolError("invalid serialization of WAMP message: {0} {1}".format(type(e).__name__, e))

        if self._serializer.NAME == 'flatbuffers':
            msgs = raw_msgs
        else:
            msgs = []
            for raw_msg in raw_msgs:

                if type(raw_msg) != list:
                    raise ProtocolError("invalid type {0} for WAMP message".format(type(raw_msg)))

                if len(raw_msg) == 0:
                    raise ProtocolError("missing message type in WAMP message")

                message_type = raw_msg[0]

                if type(message_type) != int:
                    # CBOR doesn't roundtrip number types
                    # https://bitbucket.org/bodhisnarkva/cbor/issues/6/number-types-dont-roundtrip
                    raise ProtocolError("invalid type {0} for WAMP message type".format(type(message_type)))

                Klass = self.MESSAGE_TYPE_MAP.get(message_type)

                if Klass is None:
                    raise ProtocolError("invalid WAMP message type {0}".format(message_type))

                # this might again raise `ProtocolError` ..
                msg = Klass.parse(raw_msg)

                msgs.append(msg)

        # maintain statistics for unserialized WAMP message data
        self._unserialized_bytes += len(payload)
        self._unserialized_messages += len(msgs)
        self._unserialized_rated_messages += int(math.ceil(float(len(payload)) / self.RATED_MESSAGE_SIZE))

        # maybe auto-reset and trigger user callback ..
        if self._autoreset_callback and ((self._autoreset_duration and (time_ns() - self._stats_reset) >= self._autoreset_duration) or (self._autoreset_rated_messages and self.stats_rated_messages() >= self._autoreset_rated_messages)):
            stats = self.stats(reset=True)
            self._autoreset_callback(stats)

        return msgs
Beispiel #13
0
    def unserialize(self, payload, isBinary=None):
        """
        Implements :func:`autobahn.wamp.interfaces.ISerializer.unserialize`
        """
        if isBinary is not None:
            if isBinary != self._serializer.BINARY:
                raise ProtocolError(
                    "invalid serialization of WAMP message (binary {0}, but expected {1})"
                    .format(isBinary, self._serializer.BINARY))
        try:
            raw_msgs = self._serializer.unserialize(payload)
        except Exception as e:
            raise ProtocolError(
                "invalid serialization of WAMP message: {0} {1}".format(
                    type(e).__name__, e))

        if self._serializer.NAME == u'flatbuffers':
            msgs = raw_msgs
        else:
            msgs = []
            for raw_msg in raw_msgs:

                if type(raw_msg) != list:
                    raise ProtocolError(
                        "invalid type {0} for WAMP message".format(
                            type(raw_msg)))

                if len(raw_msg) == 0:
                    raise ProtocolError(
                        u"missing message type in WAMP message")

                message_type = raw_msg[0]

                if type(message_type) not in six.integer_types:
                    # CBOR doesn't roundtrip number types
                    # https://bitbucket.org/bodhisnarkva/cbor/issues/6/number-types-dont-roundtrip
                    raise ProtocolError(
                        "invalid type {0} for WAMP message type".format(
                            type(message_type)))

                Klass = self.MESSAGE_TYPE_MAP.get(message_type)

                if Klass is None:
                    raise ProtocolError(
                        "invalid WAMP message type {0}".format(message_type))

                # this might again raise `ProtocolError` ..
                msg = Klass.parse(raw_msg)

                msgs.append(msg)

        return msgs
Beispiel #14
0
    def process(self, session, msg):
        """
      Implements :func:`autobahn.wamp.interfaces.IRouter.process`
      """
        if self.debug:
            print("Router.process: {}".format(msg))

        ## Broker
        ##
        if isinstance(msg, message.Publish):
            self._broker.processPublish(session, msg)

        elif isinstance(msg, message.Subscribe):
            self._broker.processSubscribe(session, msg)

        elif isinstance(msg, message.Unsubscribe):
            self._broker.processUnsubscribe(session, msg)

        ## Dealer
        ##
        elif isinstance(msg, message.Register):
            self._dealer.processRegister(session, msg)

        elif isinstance(msg, message.Unregister):
            self._dealer.processUnregister(session, msg)

        elif isinstance(msg, message.Call):
            self._dealer.processCall(session, msg)

        elif isinstance(msg, message.Cancel):
            self._dealer.processCancel(session, msg)

        elif isinstance(msg, message.Yield):
            self._dealer.processYield(session, msg)

        elif isinstance(
                msg, message.Error
        ) and msg.request_type == message.Invocation.MESSAGE_TYPE:
            self._dealer.processInvocationError(session, msg)

        else:
            raise ProtocolError("Unexpected message {}".format(msg.__class__))
    def processYield(self, session, yield_):
        """
      Implements :func:`autobahn.wamp.interfaces.IDealer.processYield`
      """
        assert (session in self._session_to_registrations)

        if yield_.request in self._invocations:
            call_msg, call_session = self._invocations[yield_.request]
            msg = message.Result(call_msg.request,
                                 args=yield_.args,
                                 kwargs=yield_.kwargs,
                                 progress=yield_.progress)

            ## the calling session might have been lost in the meantime ..
            if call_session._transport:
                call_session._transport.send(msg)

            if not yield_.progress:
                del self._invocations[yield_.request]
        else:
            raise ProtocolError(
                "Dealer.onYield(): YIELD received for non-pending request ID {}"
                .format(yield_.request))
    def processInvocationError(self, session, error):
        """
      Implements :func:`autobahn.wamp.interfaces.IDealer.processInvocationError`
      """
        assert (session in self._session_to_registrations)

        if error.request in self._invocations:
            call_msg, call_session = self._invocations[error.request]
            msg = message.Error(message.Call.MESSAGE_TYPE,
                                call_msg.request,
                                error.error,
                                args=error.args,
                                kwargs=error.kwargs)

            ## the calling session might have been lost in the meantime ..
            if call_session._transport:
                call_session._transport.send(msg)

            del self._invocations[error.request]
        else:
            raise ProtocolError(
                "Dealer.onInvocationError(): ERROR received for non-pending request_type {} and request ID {}"
                .format(error.request_type, error.request))
Beispiel #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)
Beispiel #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)
            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__))
Beispiel #19
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__))
Beispiel #20
0
        def send(self, msg):
            if self._log:
                payload, isbinary = self._serializer.serialize(msg)
                print("Send: {0}".format(payload))

            reply = None

            if isinstance(msg, message.Publish):
                if msg.topic.startswith(u'com.myapp'):
                    if msg.acknowledge:
                        reply = message.Published(
                            msg.request,
                            self._fake_router_session._request_id_gen.next())
                elif len(msg.topic) == 0:
                    reply = message.Error(message.Publish.MESSAGE_TYPE,
                                          msg.request,
                                          u'wamp.error.invalid_uri')
                else:
                    reply = message.Error(message.Publish.MESSAGE_TYPE,
                                          msg.request,
                                          u'wamp.error.not_authorized')

            elif isinstance(msg, message.Call):
                if msg.procedure == u'com.myapp.procedure1':
                    reply = message.Result(msg.request, args=[100])
                elif msg.procedure == u'com.myapp.procedure2':
                    reply = message.Result(msg.request, args=[1, 2, 3])
                elif msg.procedure == u'com.myapp.procedure3':
                    reply = message.Result(msg.request,
                                           args=[1, 2, 3],
                                           kwargs={
                                               u'foo': u'bar',
                                               u'baz': 23
                                           })

                elif msg.procedure.startswith(u'com.myapp.myproc'):
                    registration = self._registrations[msg.procedure]
                    request = self._fake_router_session._request_id_gen.next()
                    if request in self._invocations:
                        raise ProtocolError("duplicate invocation")
                    self._invocations[request] = msg.request
                    reply = message.Invocation(
                        request,
                        registration,
                        args=msg.args,
                        kwargs=msg.kwargs,
                        receive_progress=msg.receive_progress,
                    )
                else:
                    reply = message.Error(message.Call.MESSAGE_TYPE,
                                          msg.request,
                                          u'wamp.error.no_such_procedure')

            elif isinstance(msg, message.Yield):
                if msg.request in self._invocations:
                    request = self._invocations[msg.request]
                    reply = message.Result(request,
                                           args=msg.args,
                                           kwargs=msg.kwargs,
                                           progress=msg.progress)

            elif isinstance(msg, message.Subscribe):
                topic = msg.topic
                if topic in self._subscription_topics:
                    reply_id = self._subscription_topics[topic]
                else:
                    reply_id = self._fake_router_session._request_id_gen.next()
                    self._subscription_topics[topic] = reply_id
                reply = message.Subscribed(msg.request, reply_id)

            elif isinstance(msg, message.Unsubscribe):
                reply = message.Unsubscribed(msg.request)

            elif isinstance(msg, message.Register):
                registration = self._fake_router_session._request_id_gen.next()
                self._registrations[msg.procedure] = registration
                reply = message.Registered(msg.request, registration)

            elif isinstance(msg, message.Unregister):
                reply = message.Unregistered(msg.request)

            elif isinstance(msg, message.Error):
                # since I'm basically a Dealer, I forward on the
                # error, but reply to my own request/invocation
                request = self._invocations[msg.request]
                reply = message.Error(
                    message.Call.MESSAGE_TYPE,
                    request,
                    msg.error,
                    args=msg.args,
                    kwargs=msg.kwargs,
                )

            if reply:
                if self._log:
                    payload, isbinary = self._serializer.serialize(reply)
                    print("Receive: {0}".format(payload))
                self._handler.onMessage(reply)
Beispiel #21
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)
Beispiel #22
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)