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)
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))
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))
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
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))
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))
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))
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))
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))
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
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__))
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
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
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))
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)
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__))
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__))
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)
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)
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)