def get_session_by_session_id(self, session_id: int, joined_before: Optional[int] = None) -> Optional[Dict[str, Any]]: """ Implements :meth:`crossbar._interfaces.IRealmStore.get_session_by_session_id` """ if joined_before: _joined_before = np.datetime64(joined_before, 'ns') else: _joined_before = np.datetime64(time_ns(), 'ns') _from_key = (session_id, np.datetime64(0, 'ns')) _to_key = (session_id, _joined_before) # check if we have a record store for the session session: Optional[cfxdb.realmstore.Session] = None with self._db.begin() as txn: # lookup session by WAMP session ID and find the most recent session # according to joined_at timestamp for session_oid in self._schema.idx_sessions_by_session_id.select(txn, from_key=_from_key, to_key=_to_key, reverse=True, return_keys=False, return_values=True): session = self._schema.sessions[txn, session_oid] # if we have an index, that index must always resolve to an indexed record assert session # we only want the most recent session break if session: # extract info from database table to construct session details and return td = TransportDetails.parse(session.transport) sd = SessionDetails( realm=session.realm, session=session.session, authid=session.authid, authrole=session.authrole, authmethod=session.authmethod, authprovider=session.authprovider, authextra=session.authextra, # FIXME serializer=None, resumed=False, resumable=False, resume_token=None, transport=td) res = sd.marshal() return res else: return None
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))
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))
def test_empty(self): sd1 = SessionDetails() data = sd1.marshal() self.assertEqual(data, { 'realm': None, 'session': None, 'authid': None, 'authrole': None, 'authmethod': None, 'authprovider': None, 'authextra': None, 'serializer': None, 'transport': None, 'resumed': None, 'resumable': None, 'resume_token': None, }) sd2 = SessionDetails.parse(data) self.assertEqual(sd2, sd1)
def test_event_context(): parent = Context() session_details = SessionDetails('default', 5) event_details = EventDetails(publication=15, publisher=8, publisher_authid='user', publisher_authrole='role', topic='topic') context = EventContext(parent, session_details, event_details) assert context.session_id == 5 assert context.publisher_session_id == 8 assert context.publisher_auth_id == 'user' assert context.publisher_auth_role == 'role' assert context.publication_id == 15 assert context.topic == 'topic' assert context.enc_algo is None
def test_call_context(): def progress(arg): pass parent = Context() session_details = SessionDetails('default', 5) call_details = CallDetails(progress=progress, caller=8, caller_authid='user', caller_authrole='role', procedure='procedurename') context = CallContext(parent, session_details, call_details) assert context.session_id == 5 assert context.caller_session_id == 8 assert context.caller_auth_id == 'user' assert context.caller_auth_role == 'role' assert context.procedure == 'procedurename' assert context.enc_algo is None assert context.progress is progress
def send(self, msg): """ Implements :func:`autobahn.wamp.interfaces.ITransport.send` """ if isinstance(msg, message.Hello): self._router = self._routerFactory.get(msg.realm) # fake session ID assignment (normally done in WAMP opening handshake) self._session._session_id = util.id() # set fixed/trusted authentication information self._session._authid = self._trusted_authid self._session._authrole = self._trusted_authrole self._session._authmethod = None # FIXME: the following does blow up # self._session._authmethod = u'trusted' self._session._authprovider = None self._session._authextra = None # add app session to router self._router.attach(self._session) # fake app session open details = SessionDetails( self._session._realm, self._session._session_id, self._session._authid, self._session._authrole, self._session._authmethod, self._session._authprovider, self._session._authextra) # have to fire the 'join' notification ourselves, as we're # faking out what the protocol usually does. d = self._session.fire('join', self._session, details) d.addErrback( lambda fail: self._log_error(fail, "While notifying 'join'")) # now fire onJoin (since _log_error returns None, we'll be # back in the callback chain even on errors from 'join' d.addCallback( lambda _: txaio.as_future(self._session.onJoin, details)) d.addErrback( lambda fail: self._swallow_error(fail, "While firing onJoin")) d.addCallback(lambda _: self._session.fire('ready', self._session)) d.addErrback( lambda fail: self._log_error(fail, "While notifying 'ready'")) # app-to-router # elif isinstance(msg, (message.Publish, message.Subscribe, message.Unsubscribe, message.Call, message.Yield, message.Register, message.Unregister, message.Cancel)) or \ (isinstance(msg, message.Error) and msg.request_type == message.Invocation.MESSAGE_TYPE): # deliver message to router # self._router.process(self._session, msg) # router-to-app # elif isinstance(msg, (message.Event, message.Invocation, message.Result, message.Published, message.Subscribed, message.Unsubscribed, message.Registered, message.Unregistered)) or \ (isinstance(msg, message.Error) and (msg.request_type in { message.Call.MESSAGE_TYPE, message.Cancel.MESSAGE_TYPE, message.Register.MESSAGE_TYPE, message.Unregister.MESSAGE_TYPE, message.Publish.MESSAGE_TYPE, message.Subscribe.MESSAGE_TYPE, message.Unsubscribe.MESSAGE_TYPE})): # deliver message to app session # self._session.onMessage(msg) # ignore messages # elif isinstance(msg, message.Goodbye): details = types.CloseDetails(msg.reason, msg.message) session = self._session @inlineCallbacks def do_goodbye(): try: yield session.onLeave(details) except Exception: self._log_error(Failure(), "While firing onLeave") if session._transport: session._transport.close() try: yield session.fire('leave', session, details) except Exception: self._log_error(Failure(), "While notifying 'leave'") try: yield session.fire('disconnect', session) except Exception: self._log_error(Failure(), "While notifying 'disconnect'") if self._router._realm.session: yield self._router._realm.session.publish( u'wamp.session.on_leave', session._session_id, ) d = do_goodbye() d.addErrback(lambda fail: self._log_error(fail, "Internal error")) else: # should not arrive here # raise Exception( "RouterApplicationSession.send: unhandled message {0}".format( msg))
def send(self, msg): """ Implements :func:`autobahn.wamp.interfaces.ITransport.send` """ if isinstance(msg, message.Hello): self._router = self._routerFactory.get(msg.realm) ## fake session ID assignment (normally done in WAMP opening handshake) self._session._session_id = util.id() ## set fixed/trusted authentication information self._session._authid = self._trusted_authid self._session._authrole = self._trusted_authrole self._session._authmethod = None ## FIXME: the following does blow up #self._session._authmethod = u'trusted' self._session._authprovider = None ## add app session to router self._router.attach(self._session) ## fake app session open ## details = SessionDetails(self._session._realm, self._session._session_id, self._session._authid, self._session._authrole, self._session._authmethod, self._session._authprovider) self._session._as_future(self._session.onJoin, details) #self._session.onJoin(details) ## app-to-router ## elif isinstance(msg, message.Publish) or \ isinstance(msg, message.Subscribe) or \ isinstance(msg, message.Unsubscribe) or \ isinstance(msg, message.Call) or \ isinstance(msg, message.Yield) or \ isinstance(msg, message.Register) or \ isinstance(msg, message.Unregister) or \ isinstance(msg, message.Cancel) or \ (isinstance(msg, message.Error) and msg.request_type == message.Invocation.MESSAGE_TYPE): ## deliver message to router ## self._router.process(self._session, msg) ## router-to-app ## elif isinstance(msg, message.Event) or \ isinstance(msg, message.Invocation) or \ isinstance(msg, message.Result) or \ isinstance(msg, message.Published) or \ isinstance(msg, message.Subscribed) or \ isinstance(msg, message.Unsubscribed) or \ isinstance(msg, message.Registered) or \ isinstance(msg, message.Unregistered) or \ (isinstance(msg, message.Error) and ( msg.request_type == message.Call.MESSAGE_TYPE or msg.request_type == message.Cancel.MESSAGE_TYPE or msg.request_type == message.Register.MESSAGE_TYPE or msg.request_type == message.Unregister.MESSAGE_TYPE or msg.request_type == message.Publish.MESSAGE_TYPE or msg.request_type == message.Subscribe.MESSAGE_TYPE or msg.request_type == message.Unsubscribe.MESSAGE_TYPE)): ## deliver message to app session ## self._session.onMessage(msg) else: ## should not arrive here ## raise Exception( "RouterApplicationSession.send: unhandled message {0}".format( msg))
def send(self, msg): """ Implements :func:`autobahn.wamp.interfaces.ITransport.send` """ if isinstance(msg, message.Hello): self._router = self._routerFactory.get(msg.realm) # fake session ID assignment (normally done in WAMP opening handshake) self._session._session_id = util.id() # set fixed/trusted authentication information self._session._authid = self._trusted_authid self._session._authrole = self._trusted_authrole self._session._authmethod = None # FIXME: the following does blow up # self._session._authmethod = u'trusted' self._session._authprovider = None # add app session to router self._router.attach(self._session) # fake app session open # details = SessionDetails(self._session._realm, self._session._session_id, self._session._authid, self._session._authrole, self._session._authmethod, self._session._authprovider) # fire onOpen callback and handle any exception escaping from there d = txaio.as_future(self._session.onJoin, details) txaio.add_callbacks( d, None, lambda fail: self._swallow_error(fail, "While firing onJoin")) # app-to-router # elif isinstance(msg, (message.Publish, message.Subscribe, message.Unsubscribe, message.Call, message.Yield, message.Register, message.Unregister, message.Cancel)) or \ (isinstance(msg, message.Error) and msg.request_type == message.Invocation.MESSAGE_TYPE): # deliver message to router # self._router.process(self._session, msg) # router-to-app # elif isinstance(msg, (message.Event, message.Invocation, message.Result, message.Published, message.Subscribed, message.Unsubscribed, message.Registered, message.Unregistered)) or \ (isinstance(msg, message.Error) and (msg.request_type in { message.Call.MESSAGE_TYPE, message.Cancel.MESSAGE_TYPE, message.Register.MESSAGE_TYPE, message.Unregister.MESSAGE_TYPE, message.Publish.MESSAGE_TYPE, message.Subscribe.MESSAGE_TYPE, message.Unsubscribe.MESSAGE_TYPE})): # deliver message to app session # self._session.onMessage(msg) # ignore messages # elif isinstance(msg, message.Goodbye): # fire onClose callback and handle any exception escaping from there d = txaio.as_future(self._session.onClose, None) txaio.add_callbacks( d, None, lambda fail: self._swallow_error(fail, "While firing onClose")) else: # should not arrive here # raise Exception( "RouterApplicationSession.send: unhandled message {0}".format( 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) 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 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 test_attributes(self): sd1 = SessionDetails() td1 = TransportDetails.parse(TRANSPORT_DETAILS_1) sd1.realm = 'realm1' sd1.session = 666 sd1.authid = 'homer' sd1.authrole = 'user' sd1.authmethod = 'wampcra' sd1.authprovider = 'static' sd1.authextra = {'foo': 'bar', 'baz': [1, 2, 3]} sd1.serializer = 'json' sd1.transport = td1 sd1.resumed = False sd1.resumable = True sd1.resume_token = '8713e25a-d4f5-48b7-9d6d-eda66603a1ab' data = sd1.marshal() self.assertEqual( data, { 'realm': sd1.realm, 'session': sd1.session, 'authid': sd1.authid, 'authrole': sd1.authrole, 'authmethod': sd1.authmethod, 'authprovider': sd1.authprovider, 'authextra': sd1.authextra, 'serializer': sd1.serializer, 'transport': sd1.transport.marshal(), 'resumed': sd1.resumed, 'resumable': sd1.resumable, 'resume_token': sd1.resume_token, }) sd2 = SessionDetails.parse(data) self.assertEqual(sd2, sd1)
def test_join_event(wampclient): details = SessionDetails('default', 5) event = SessionJoinEvent(wampclient, 'realm_joined', details) assert event.session_id == 5