1############################################################################### 2# 3# The MIT License (MIT) 4# 5# Copyright (c) Crossbar.io Technologies GmbH 6# 7# Permission is hereby granted, free of charge, to any person obtaining a copy 8# of this software and associated documentation files (the "Software"), to deal 9# in the Software without restriction, including without limitation the rights 10# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11# copies of the Software, and to permit persons to whom the Software is 12# furnished to do so, subject to the following conditions: 13# 14# The above copyright notice and this permission notice shall be included in 15# all copies or substantial portions of the Software. 16# 17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23# THE SOFTWARE. 24# 25############################################################################### 26 27from __future__ import absolute_import 28 29import re 30import binascii 31 32import six 33 34import autobahn 35from autobahn.wamp.exception import ProtocolError 36from autobahn.wamp.role import ROLE_NAME_TO_CLASS 37 38try: 39 import cbor 40 import flatbuffers 41 from autobahn.wamp import message_fbs 42except ImportError: 43 _HAS_WAMP_FLATBUFFERS = False 44else: 45 _HAS_WAMP_FLATBUFFERS = True 46 47__all__ = ('Message', 48 'Hello', 49 'Welcome', 50 'Abort', 51 'Challenge', 52 'Authenticate', 53 'Goodbye', 54 'Error', 55 'Publish', 56 'Published', 57 'Subscribe', 58 'Subscribed', 59 'Unsubscribe', 60 'Unsubscribed', 61 'Event', 62 'Call', 63 'Cancel', 64 'Result', 65 'Register', 66 'Registered', 67 'Unregister', 68 'Unregistered', 69 'Invocation', 70 'Interrupt', 71 'Yield', 72 'check_or_raise_uri', 73 'check_or_raise_id', 74 'is_valid_enc_algo', 75 'is_valid_enc_serializer', 76 'PAYLOAD_ENC_CRYPTO_BOX', 77 'PAYLOAD_ENC_MQTT', 78 'PAYLOAD_ENC_STANDARD_IDENTIFIERS') 79 80 81# strict URI check allowing empty URI components 82_URI_PAT_STRICT_EMPTY = re.compile(r"^(([0-9a-z_]+\.)|\.)*([0-9a-z_]+)?$") 83 84# loose URI check allowing empty URI components 85_URI_PAT_LOOSE_EMPTY = re.compile(r"^(([^\s\.#]+\.)|\.)*([^\s\.#]+)?$") 86 87# strict URI check disallowing empty URI components 88_URI_PAT_STRICT_NON_EMPTY = re.compile(r"^([0-9a-z_]+\.)*([0-9a-z_]+)$") 89 90# loose URI check disallowing empty URI components 91_URI_PAT_LOOSE_NON_EMPTY = re.compile(r"^([^\s\.#]+\.)*([^\s\.#]+)$") 92 93# strict URI check disallowing empty URI components in all but the last component 94_URI_PAT_STRICT_LAST_EMPTY = re.compile(r"^([0-9a-z_]+\.)*([0-9a-z_]*)$") 95 96# loose URI check disallowing empty URI components in all but the last component 97_URI_PAT_LOOSE_LAST_EMPTY = re.compile(r"^([^\s\.#]+\.)*([^\s\.#]*)$") 98 99# custom (=implementation specific) WAMP attributes (used in WAMP message details/options) 100_CUSTOM_ATTRIBUTE = re.compile(r"^x_([a-z][0-9a-z_]+)?$") 101 102# Value for algo attribute in end-to-end encrypted messages using cryptobox, which 103# is a scheme based on Curve25519, SHA512, Salsa20 and Poly1305. 104# See: http://cr.yp.to/highspeed/coolnacl-20120725.pdf 105PAYLOAD_ENC_CRYPTO_BOX = u'cryptobox' 106 107# Payload transparency identifier for MQTT payloads (which are arbitrary binary). 108PAYLOAD_ENC_MQTT = u'mqtt' 109 110# Payload transparency identifier for XBR payloads 111PAYLOAD_ENC_XBR = u'xbr' 112 113# Payload transparency algorithm identifiers from the WAMP spec. 114PAYLOAD_ENC_STANDARD_IDENTIFIERS = [PAYLOAD_ENC_CRYPTO_BOX, PAYLOAD_ENC_MQTT, PAYLOAD_ENC_XBR] 115 116# Payload transparency serializer identifiers from the WAMP spec. 117PAYLOAD_ENC_STANDARD_SERIALIZERS = [u'json', u'msgpack', u'cbor', u'ubjson', u'flatbuffers'] 118 119ENC_ALGO_NONE = 0 120ENC_ALGO_CRYPTOBOX = 1 121ENC_ALGO_MQTT = 2 122ENC_ALGO_XBR = 3 123 124ENC_ALGOS = { 125 ENC_ALGO_NONE: u'null', 126 ENC_ALGO_CRYPTOBOX: u'cryptobox', 127 ENC_ALGO_MQTT: u'mqtt', 128 ENC_ALGO_XBR: u'xbr', 129} 130 131ENC_ALGOS_FROMSTR = {key: value for value, key in ENC_ALGOS.items()} 132 133ENC_SER_NONE = 0 134ENC_SER_JSON = 1 135ENC_SER_MSGPACK = 2 136ENC_SER_CBOR = 3 137ENC_SER_UBJSON = 4 138ENC_SER_OPAQUE = 5 139ENC_SER_FLATBUFFERS = 6 140 141ENC_SERS = { 142 ENC_SER_NONE: u'null', 143 ENC_SER_JSON: u'json', 144 ENC_SER_MSGPACK: u'msgpack', 145 ENC_SER_CBOR: u'cbor', 146 ENC_SER_UBJSON: u'ubjson', 147 ENC_SER_OPAQUE: u'opaque', 148 ENC_SER_FLATBUFFERS: u'flatbuffers', 149} 150 151ENC_SERS_FROMSTR = {key: value for value, key in ENC_SERS.items()} 152 153 154def is_valid_enc_algo(enc_algo): 155 """ 156 For WAMP payload transparency mode, check if the provided ``enc_algo`` 157 identifier in the WAMP message is a valid one. 158 159 Currently defined standard identifiers are: 160 161 * ``"cryptobox"`` 162 * ``"mqtt"`` 163 * ``"xbr"`` 164 165 Users can select arbitrary identifiers too, but these MUST start with ``u"x_"``. 166 167 :param enc_algo: The payload transparency algorithm identifier to check. 168 :type enc_algo: str 169 170 :returns: Returns ``True`` if and only if the payload transparency 171 algorithm identifier is valid. 172 :rtype: bool 173 """ 174 return type(enc_algo) == six.text_type and (enc_algo in PAYLOAD_ENC_STANDARD_IDENTIFIERS or _CUSTOM_ATTRIBUTE.match(enc_algo)) 175 176 177def is_valid_enc_serializer(enc_serializer): 178 """ 179 For WAMP payload transparency mode, check if the provided ``enc_serializer`` 180 identifier in the WAMP message is a valid one. 181 182 Currently, the only standard defined identifier are 183 184 * ``"json"`` 185 * ``"msgpack"`` 186 * ``"cbor"`` 187 * ``"ubjson"`` 188 * ``"flatbuffers"`` 189 190 Users can select arbitrary identifiers too, but these MUST start with ``u"x_"``. 191 192 :param enc_serializer: The payload transparency serializer identifier to check. 193 :type enc_serializer: str 194 195 :returns: Returns ``True`` if and only if the payload transparency 196 serializer identifier is valid. 197 :rtype: bool 198 """ 199 return type(enc_serializer) == six.text_type and (enc_serializer in PAYLOAD_ENC_STANDARD_SERIALIZERS or _CUSTOM_ATTRIBUTE.match(enc_serializer)) 200 201 202def b2a(data, max_len=40): 203 if type(data) == six.text_type: 204 s = data 205 elif type(data) == six.binary_type: 206 s = binascii.b2a_hex(data).decode('ascii') 207 elif data is None: 208 s = u'-' 209 else: 210 s = u'{}'.format(data) 211 if len(s) > max_len: 212 return s[:max_len] + u'..' 213 else: 214 return s 215 216 217def check_or_raise_uri(value, message=u"WAMP message invalid", strict=False, allow_empty_components=False, allow_last_empty=False, allow_none=False): 218 """ 219 Check a value for being a valid WAMP URI. 220 221 If the value is not a valid WAMP URI is invalid, raises :class:`autobahn.wamp.exception.ProtocolError`. 222 Otherwise return the value. 223 224 :param value: The value to check. 225 :type value: str or None 226 227 :param message: Prefix for message in exception raised when value is invalid. 228 :type message: str 229 230 :param strict: If ``True``, do a strict check on the URI (the WAMP spec SHOULD behavior). 231 :type strict: bool 232 233 :param allow_empty_components: If ``True``, allow empty URI components (for pattern based 234 subscriptions and registrations). 235 :type allow_empty_components: bool 236 237 :param allow_none: If ``True``, allow ``None`` for URIs. 238 :type allow_none: bool 239 240 :returns: The URI value (if valid). 241 :rtype: str 242 243 :raises: instance of :class:`autobahn.wamp.exception.ProtocolError` 244 """ 245 if value is None: 246 if allow_none: 247 return 248 else: 249 raise ProtocolError(u"{0}: URI cannot be null".format(message)) 250 251 if type(value) != six.text_type: 252 if not (value is None and allow_none): 253 raise ProtocolError(u"{0}: invalid type {1} for URI".format(message, type(value))) 254 255 if strict: 256 if allow_last_empty: 257 pat = _URI_PAT_STRICT_LAST_EMPTY 258 elif allow_empty_components: 259 pat = _URI_PAT_STRICT_EMPTY 260 else: 261 pat = _URI_PAT_STRICT_NON_EMPTY 262 else: 263 if allow_last_empty: 264 pat = _URI_PAT_LOOSE_LAST_EMPTY 265 elif allow_empty_components: 266 pat = _URI_PAT_LOOSE_EMPTY 267 else: 268 pat = _URI_PAT_LOOSE_NON_EMPTY 269 270 if not pat.match(value): 271 raise ProtocolError(u"{0}: invalid value '{1}' for URI (did not match pattern {2}, strict={3}, allow_empty_components={4}, allow_last_empty={5}, allow_none={6})".format(message, value, pat.pattern, strict, allow_empty_components, allow_last_empty, allow_none)) 272 else: 273 return value 274 275 276def check_or_raise_id(value, message=u"WAMP message invalid"): 277 """ 278 Check a value for being a valid WAMP ID. 279 280 If the value is not a valid WAMP ID, raises :class:`autobahn.wamp.exception.ProtocolError`. 281 Otherwise return the value. 282 283 :param value: The value to check. 284 :type value: int 285 286 :param message: Prefix for message in exception raised when value is invalid. 287 :type message: str 288 289 :returns: The ID value (if valid). 290 :rtype: int 291 292 :raises: instance of :class:`autobahn.wamp.exception.ProtocolError` 293 """ 294 if type(value) not in six.integer_types: 295 raise ProtocolError(u"{0}: invalid type {1} for ID".format(message, type(value))) 296 # the value 0 for WAMP IDs is possible in certain WAMP messages, e.g. UNREGISTERED with 297 # router revocation signaling! 298 if value < 0 or value > 9007199254740992: # 2**53 299 raise ProtocolError(u"{0}: invalid value {1} for ID".format(message, value)) 300 return value 301 302 303def check_or_raise_extra(value, message=u"WAMP message invalid"): 304 """ 305 Check a value for being a valid WAMP extra dictionary. 306 307 If the value is not a valid WAMP extra dictionary, raises :class:`autobahn.wamp.exception.ProtocolError`. 308 Otherwise return the value. 309 310 :param value: The value to check. 311 :type value: dict 312 313 :param message: Prefix for message in exception raised when value is invalid. 314 :type message: str 315 316 :returns: The extra dictionary (if valid). 317 :rtype: dict 318 319 :raises: instance of :class:`autobahn.wamp.exception.ProtocolError` 320 """ 321 if type(value) != dict: 322 raise ProtocolError(u"{0}: invalid type {1} for WAMP extra".format(message, type(value))) 323 for k in value.keys(): 324 if not isinstance(k, six.text_type): 325 raise ProtocolError(u"{0}: invalid type {1} for key in WAMP extra ('{2}')".format(message, type(k), k)) 326 return value 327 328 329def _validate_kwargs(kwargs, message=u"WAMP message invalid"): 330 """ 331 Check a value for being a valid WAMP kwargs dictionary. 332 333 If the value is not a valid WAMP kwargs dictionary, 334 raises :class:`autobahn.wamp.exception.ProtocolError`. 335 Otherwise return the kwargs. 336 337 The WAMP spec requires that the keys in kwargs are proper 338 strings (unicode), not bytes. Note that the WAMP spec 339 says nothing about keys in application payload. Key in the 340 latter can be potentially of other type (if that is really 341 wanted). 342 343 :param kwargs: The keyword arguments to check. 344 :type kwargs: dict 345 346 :param message: Prefix for message in exception raised when 347 value is invalid. 348 :type message: str 349 350 :returns: The kwargs dictionary (if valid). 351 :rtype: dict 352 353 :raises: instance of 354 :class:`autobahn.wamp.exception.ProtocolError` 355 """ 356 if kwargs is not None: 357 if type(kwargs) != dict: 358 raise ProtocolError(u"{0}: invalid type {1} for WAMP kwargs".format(message, type(kwargs))) 359 for k in kwargs.keys(): 360 if not isinstance(k, six.text_type): 361 raise ProtocolError(u"{0}: invalid type {1} for key in WAMP kwargs ('{2}')".format(message, type(k), k)) 362 return kwargs 363 364 365class Message(object): 366 """ 367 WAMP message base class. 368 369 .. note:: This is not supposed to be instantiated, but subclassed only. 370 """ 371 372 MESSAGE_TYPE = None 373 """ 374 WAMP message type code. 375 """ 376 377 __slots__ = ( 378 '_from_fbs', 379 '_serialized', 380 '_correlation_id', 381 '_correlation_uri', 382 '_correlation_is_anchor', 383 '_correlation_is_last', 384 ) 385 386 def __init__(self, from_fbs=None): 387 # only filled in case this object has flatbuffers underlying 388 self._from_fbs = from_fbs 389 390 # serialization cache: mapping from ISerializer instances to serialized bytes 391 self._serialized = {} 392 393 # user attributes for message correlation (mainly for message tracing) 394 self._correlation_id = None 395 self._correlation_uri = None 396 self._correlation_is_anchor = None 397 self._correlation_is_last = None 398 399 @property 400 def correlation_id(self): 401 return self._correlation_id 402 403 @correlation_id.setter 404 def correlation_id(self, value): 405 assert(value is None or type(value) == six.text_type) 406 self._correlation_id = value 407 408 @property 409 def correlation_uri(self): 410 return self._correlation_uri 411 412 @correlation_uri.setter 413 def correlation_uri(self, value): 414 assert(value is None or type(value) == six.text_type) 415 self._correlation_uri = value 416 417 @property 418 def correlation_is_anchor(self): 419 return self._correlation_is_anchor 420 421 @correlation_is_anchor.setter 422 def correlation_is_anchor(self, value): 423 assert(value is None or type(value) == bool) 424 self._correlation_is_anchor = value 425 426 @property 427 def correlation_is_last(self): 428 return self._correlation_is_last 429 430 @correlation_is_last.setter 431 def correlation_is_last(self, value): 432 assert(value is None or type(value) == bool) 433 self._correlation_is_last = value 434 435 def __eq__(self, other): 436 """ 437 Compare this message to another message for equality. 438 439 :param other: The other message to compare with. 440 :type other: obj 441 442 :returns: ``True`` iff the messages are equal. 443 :rtype: bool 444 """ 445 if not isinstance(other, self.__class__): 446 return False 447 # we only want the actual message data attributes (not eg _serialize) 448 for k in self.__slots__: 449 if k not in ['_serialized', 450 '_correlation_id', 451 '_correlation_uri', 452 '_correlation_is_anchor', 453 '_correlation_is_last'] and not k.startswith('_'): 454 if not getattr(self, k) == getattr(other, k): 455 return False 456 return True 457 458 def __ne__(self, other): 459 """ 460 Compare this message to another message for inequality. 461 462 :param other: The other message to compare with. 463 :type other: obj 464 465 :returns: ``True`` iff the messages are not equal. 466 :rtype: bool 467 """ 468 return not self.__eq__(other) 469 470 @staticmethod 471 def parse(wmsg): 472 """ 473 Factory method that parses a unserialized raw message (as returned byte 474 :func:`autobahn.interfaces.ISerializer.unserialize`) into an instance 475 of this class. 476 477 :returns: An instance of this class. 478 :rtype: obj 479 """ 480 raise NotImplementedError() 481 482 def marshal(self): 483 raise NotImplementedError() 484 485 @staticmethod 486 def cast(buf): 487 raise NotImplementedError() 488 489 def build(self, builder): 490 raise NotImplementedError() 491 492 def uncache(self): 493 """ 494 Resets the serialization cache. 495 """ 496 self._serialized = {} 497 498 def serialize(self, serializer): 499 """ 500 Serialize this object into a wire level bytes representation and cache 501 the resulting bytes. If the cache already contains an entry for the given 502 serializer, return the cached representation directly. 503 504 :param serializer: The wire level serializer to use. 505 :type serializer: An instance that implements :class:`autobahn.interfaces.ISerializer` 506 507 :returns: The serialized bytes. 508 :rtype: bytes 509 """ 510 # only serialize if not cached .. 511 if serializer not in self._serialized: 512 if serializer.NAME == u'flatbuffers': 513 # flatbuffers get special treatment .. 514 builder = flatbuffers.Builder(1024) 515 516 # this is the core method writing out this message (self) to a (new) flatbuffer 517 # FIXME: implement this method for all classes derived from Message 518 obj = self.build(builder) 519 520 builder.Finish(obj) 521 buf = builder.Output() 522 self._serialized[serializer] = bytes(buf) 523 else: 524 # all other serializers first marshal() the object and then serialize the latter 525 self._serialized[serializer] = serializer.serialize(self.marshal()) 526 527 # cache is filled now: return serialized, cached bytes 528 return self._serialized[serializer] 529 530 531class Hello(Message): 532 """ 533 A WAMP ``HELLO`` message. 534 535 Format: ``[HELLO, Realm|uri, Details|dict]`` 536 """ 537 538 MESSAGE_TYPE = 1 539 """ 540 The WAMP message code for this type of message. 541 """ 542 543 __slots__ = ( 544 'realm', 545 'roles', 546 'authmethods', 547 'authid', 548 'authrole', 549 'authextra', 550 'resumable', 551 'resume_session', 552 'resume_token', 553 ) 554 555 def __init__(self, 556 realm, 557 roles, 558 authmethods=None, 559 authid=None, 560 authrole=None, 561 authextra=None, 562 resumable=None, 563 resume_session=None, 564 resume_token=None): 565 """ 566 567 :param realm: The URI of the WAMP realm to join. 568 :type realm: str 569 570 :param roles: The WAMP session roles and features to announce. 571 :type roles: dict of :class:`autobahn.wamp.role.RoleFeatures` 572 573 :param authmethods: The authentication methods to announce. 574 :type authmethods: list of str or None 575 576 :param authid: The authentication ID to announce. 577 :type authid: str or None 578 579 :param authrole: The authentication role to announce. 580 :type authrole: str or None 581 582 :param authextra: Application-specific "extra data" to be forwarded to the client. 583 :type authextra: dict or None 584 585 :param resumable: Whether the client wants this to be a session that can be later resumed. 586 :type resumable: bool or None 587 588 :param resume_session: The session the client would like to resume. 589 :type resume_session: int or None 590 591 :param resume_token: The secure authorisation token to resume the session. 592 :type resume_token: str or None 593 """ 594 assert(realm is None or isinstance(realm, six.text_type)) 595 assert(type(roles) == dict) 596 assert(len(roles) > 0) 597 for role in roles: 598 assert(role in [u'subscriber', u'publisher', u'caller', u'callee']) 599 assert(isinstance(roles[role], autobahn.wamp.role.ROLE_NAME_TO_CLASS[role])) 600 if authmethods: 601 assert(type(authmethods) == list) 602 for authmethod in authmethods: 603 assert(type(authmethod) == six.text_type) 604 assert(authid is None or type(authid) == six.text_type) 605 assert(authrole is None or type(authrole) == six.text_type) 606 assert(authextra is None or type(authextra) == dict) 607 assert(resumable is None or type(resumable) == bool) 608 assert(resume_session is None or type(resume_session) == int) 609 assert(resume_token is None or type(resume_token) == six.text_type) 610 611 Message.__init__(self) 612 self.realm = realm 613 self.roles = roles 614 self.authmethods = authmethods 615 self.authid = authid 616 self.authrole = authrole 617 self.authextra = authextra 618 self.resumable = resumable 619 self.resume_session = resume_session 620 self.resume_token = resume_token 621 622 @staticmethod 623 def parse(wmsg): 624 """ 625 Verifies and parses an unserialized raw message into an actual WAMP message instance. 626 627 :param wmsg: The unserialized raw message. 628 :type wmsg: list 629 630 :returns: An instance of this class. 631 """ 632 # this should already be verified by WampSerializer.unserialize 633 assert(len(wmsg) > 0 and wmsg[0] == Hello.MESSAGE_TYPE) 634 635 if len(wmsg) != 3: 636 raise ProtocolError("invalid message length {0} for HELLO".format(len(wmsg))) 637 638 realm = check_or_raise_uri(wmsg[1], u"'realm' in HELLO", allow_none=True) 639 details = check_or_raise_extra(wmsg[2], u"'details' in HELLO") 640 641 roles = {} 642 643 if u'roles' not in details: 644 raise ProtocolError(u"missing mandatory roles attribute in options in HELLO") 645 646 details_roles = check_or_raise_extra(details[u'roles'], u"'roles' in 'details' in HELLO") 647 648 if len(details_roles) == 0: 649 raise ProtocolError(u"empty 'roles' in 'details' in HELLO") 650 651 for role in details_roles: 652 if role not in [u'subscriber', u'publisher', u'caller', u'callee']: 653 raise ProtocolError("invalid role '{0}' in 'roles' in 'details' in HELLO".format(role)) 654 655 role_cls = ROLE_NAME_TO_CLASS[role] 656 657 details_role = check_or_raise_extra(details_roles[role], "role '{0}' in 'roles' in 'details' in HELLO".format(role)) 658 659 if u'features' in details_role: 660 check_or_raise_extra(details_role[u'features'], "'features' in role '{0}' in 'roles' in 'details' in HELLO".format(role)) 661 662 role_features = role_cls(**details_role[u'features']) 663 664 else: 665 role_features = role_cls() 666 667 roles[role] = role_features 668 669 authmethods = None 670 if u'authmethods' in details: 671 details_authmethods = details[u'authmethods'] 672 if type(details_authmethods) != list: 673 raise ProtocolError("invalid type {0} for 'authmethods' detail in HELLO".format(type(details_authmethods))) 674 675 for auth_method in details_authmethods: 676 if type(auth_method) != six.text_type: 677 raise ProtocolError("invalid type {0} for item in 'authmethods' detail in HELLO".format(type(auth_method))) 678 679 authmethods = details_authmethods 680 681 authid = None 682 if u'authid' in details: 683 details_authid = details[u'authid'] 684 if type(details_authid) != six.text_type: 685 raise ProtocolError("invalid type {0} for 'authid' detail in HELLO".format(type(details_authid))) 686 687 authid = details_authid 688 689 authrole = None 690 if u'authrole' in details: 691 details_authrole = details[u'authrole'] 692 if type(details_authrole) != six.text_type: 693 raise ProtocolError("invalid type {0} for 'authrole' detail in HELLO".format(type(details_authrole))) 694 695 authrole = details_authrole 696 697 authextra = None 698 if u'authextra' in details: 699 details_authextra = details[u'authextra'] 700 if type(details_authextra) != dict: 701 raise ProtocolError("invalid type {0} for 'authextra' detail in HELLO".format(type(details_authextra))) 702 703 authextra = details_authextra 704 705 resumable = None 706 if u'resumable' in details: 707 resumable = details[u'resumable'] 708 if type(resumable) != bool: 709 raise ProtocolError("invalid type {0} for 'resumable' detail in HELLO".format(type(resumable))) 710 711 resume_session = None 712 if u'resume-session' in details: 713 resume_session = details[u'resume-session'] 714 if type(resume_session) != int: 715 raise ProtocolError("invalid type {0} for 'resume-session' detail in HELLO".format(type(resume_session))) 716 717 resume_token = None 718 if u'resume-token' in details: 719 resume_token = details[u'resume-token'] 720 if type(resume_token) != six.text_type: 721 raise ProtocolError("invalid type {0} for 'resume-token' detail in HELLO".format(type(resume_token))) 722 else: 723 if resume_session: 724 raise ProtocolError("resume-token must be provided if resume-session is provided in HELLO") 725 726 obj = Hello(realm, roles, authmethods, authid, authrole, authextra, resumable, resume_session, resume_token) 727 728 return obj 729 730 def marshal(self): 731 """ 732 Marshal this object into a raw message for subsequent serialization to bytes. 733 734 :returns: The serialized raw message. 735 :rtype: list 736 """ 737 details = {u'roles': {}} 738 for role in self.roles.values(): 739 details[u'roles'][role.ROLE] = {} 740 for feature in role.__dict__: 741 if not feature.startswith('_') and feature != 'ROLE' and getattr(role, feature) is not None: 742 if u'features' not in details[u'roles'][role.ROLE]: 743 details[u'roles'][role.ROLE] = {u'features': {}} 744 details[u'roles'][role.ROLE][u'features'][six.u(feature)] = getattr(role, feature) 745 746 if self.authmethods is not None: 747 details[u'authmethods'] = self.authmethods 748 749 if self.authid is not None: 750 details[u'authid'] = self.authid 751 752 if self.authrole is not None: 753 details[u'authrole'] = self.authrole 754 755 if self.authextra is not None: 756 details[u'authextra'] = self.authextra 757 758 if self.resumable is not None: 759 details[u'resumable'] = self.resumable 760 761 if self.resume_session is not None: 762 details[u'resume-session'] = self.resume_session 763 764 if self.resume_token is not None: 765 details[u'resume-token'] = self.resume_token 766 767 return [Hello.MESSAGE_TYPE, self.realm, details] 768 769 def __str__(self): 770 """ 771 Return a string representation of this message. 772 """ 773 return u"Hello(realm={}, roles={}, authmethods={}, authid={}, authrole={}, authextra={}, resumable={}, resume_session={}, resume_token={})".format(self.realm, self.roles, self.authmethods, self.authid, self.authrole, self.authextra, self.resumable, self.resume_session, self.resume_token) 774 775 776class Welcome(Message): 777 """ 778 A WAMP ``WELCOME`` message. 779 780 Format: ``[WELCOME, Session|id, Details|dict]`` 781 """ 782 783 MESSAGE_TYPE = 2 784 """ 785 The WAMP message code for this type of message. 786 """ 787 788 __slots__ = ( 789 'session', 790 'roles', 791 'realm', 792 'authid', 793 'authrole', 794 'authmethod', 795 'authprovider', 796 'authextra', 797 'resumed', 798 'resumable', 799 'resume_token', 800 'custom', 801 ) 802 803 def __init__(self, 804 session, 805 roles, 806 realm=None, 807 authid=None, 808 authrole=None, 809 authmethod=None, 810 authprovider=None, 811 authextra=None, 812 resumed=None, 813 resumable=None, 814 resume_token=None, 815 custom=None): 816 """ 817 818 :param session: The WAMP session ID the other peer is assigned. 819 :type session: int 820 821 :param roles: The WAMP roles to announce. 822 :type roles: dict of :class:`autobahn.wamp.role.RoleFeatures` 823 824 :param realm: The effective realm the session is joined on. 825 :type realm: str or None 826 827 :param authid: The authentication ID assigned. 828 :type authid: str or None 829 830 :param authrole: The authentication role assigned. 831 :type authrole: str or None 832 833 :param authmethod: The authentication method in use. 834 :type authmethod: str or None 835 836 :param authprovider: The authentication provided in use. 837 :type authprovider: str or None 838 839 :param authextra: Application-specific "extra data" to be forwarded to the client. 840 :type authextra: arbitrary or None 841 842 :param resumed: Whether the session is a resumed one. 843 :type resumed: bool or None 844 845 :param resumable: Whether this session can be resumed later. 846 :type resumable: bool or None 847 848 :param resume_token: The secure authorisation token to resume the session. 849 :type resume_token: str or None 850 851 :param custom: Implementation-specific "custom attributes" (`x_my_impl_attribute`) to be set. 852 :type custom: dict or None 853 """ 854 assert(type(session) in six.integer_types) 855 assert(type(roles) == dict) 856 assert(len(roles) > 0) 857 for role in roles: 858 assert(role in [u'broker', u'dealer']) 859 assert(isinstance(roles[role], autobahn.wamp.role.ROLE_NAME_TO_CLASS[role])) 860 assert(realm is None or type(realm) == six.text_type) 861 assert(authid is None or type(authid) == six.text_type) 862 assert(authrole is None or type(authrole) == six.text_type) 863 assert(authmethod is None or type(authmethod) == six.text_type) 864 assert(authprovider is None or type(authprovider) == six.text_type) 865 assert(authextra is None or type(authextra) == dict) 866 assert(resumed is None or type(resumed) == bool) 867 assert(resumable is None or type(resumable) == bool) 868 assert(resume_token is None or type(resume_token) == six.text_type) 869 assert(custom is None or type(custom) == dict) 870 if custom: 871 for k in custom: 872 assert(_CUSTOM_ATTRIBUTE.match(k)) 873 874 Message.__init__(self) 875 self.session = session 876 self.roles = roles 877 self.realm = realm 878 self.authid = authid 879 self.authrole = authrole 880 self.authmethod = authmethod 881 self.authprovider = authprovider 882 self.authextra = authextra 883 self.resumed = resumed 884 self.resumable = resumable 885 self.resume_token = resume_token 886 self.custom = custom or {} 887 888 @staticmethod 889 def parse(wmsg): 890 """ 891 Verifies and parses an unserialized raw message into an actual WAMP message instance. 892 893 :param wmsg: The unserialized raw message. 894 :type wmsg: list 895 896 :returns: An instance of this class. 897 """ 898 # this should already be verified by WampSerializer.unserialize 899 assert(len(wmsg) > 0 and wmsg[0] == Welcome.MESSAGE_TYPE) 900 901 if len(wmsg) != 3: 902 raise ProtocolError("invalid message length {0} for WELCOME".format(len(wmsg))) 903 904 session = check_or_raise_id(wmsg[1], u"'session' in WELCOME") 905 details = check_or_raise_extra(wmsg[2], u"'details' in WELCOME") 906 907 # FIXME: tigher value checking (types, URIs etc) 908 realm = details.get(u'realm', None) 909 authid = details.get(u'authid', None) 910 authrole = details.get(u'authrole', None) 911 authmethod = details.get(u'authmethod', None) 912 authprovider = details.get(u'authprovider', None) 913 authextra = details.get(u'authextra', None) 914 915 resumed = None 916 if u'resumed' in details: 917 resumed = details[u'resumed'] 918 if not type(resumed) == bool: 919 raise ProtocolError("invalid type {0} for 'resumed' detail in WELCOME".format(type(resumed))) 920 921 resumable = None 922 if u'resumable' in details: 923 resumable = details[u'resumable'] 924 if not type(resumable) == bool: 925 raise ProtocolError("invalid type {0} for 'resumable' detail in WELCOME".format(type(resumable))) 926 927 resume_token = None 928 if u'resume_token' in details: 929 resume_token = details[u'resume_token'] 930 if not type(resume_token) == six.text_type: 931 raise ProtocolError("invalid type {0} for 'resume_token' detail in WELCOME".format(type(resume_token))) 932 elif resumable: 933 raise ProtocolError("resume_token required when resumable is given in WELCOME") 934 935 roles = {} 936 937 if u'roles' not in details: 938 raise ProtocolError(u"missing mandatory roles attribute in options in WELCOME") 939 940 details_roles = check_or_raise_extra(details['roles'], u"'roles' in 'details' in WELCOME") 941 942 if len(details_roles) == 0: 943 raise ProtocolError(u"empty 'roles' in 'details' in WELCOME") 944 945 for role in details_roles: 946 if role not in [u'broker', u'dealer']: 947 raise ProtocolError("invalid role '{0}' in 'roles' in 'details' in WELCOME".format(role)) 948 949 role_cls = ROLE_NAME_TO_CLASS[role] 950 951 details_role = check_or_raise_extra(details_roles[role], "role '{0}' in 'roles' in 'details' in WELCOME".format(role)) 952 953 if u'features' in details_role: 954 check_or_raise_extra(details_role[u'features'], "'features' in role '{0}' in 'roles' in 'details' in WELCOME".format(role)) 955 956 role_features = role_cls(**details_roles[role][u'features']) 957 958 else: 959 role_features = role_cls() 960 961 roles[role] = role_features 962 963 custom = {} 964 for k in details: 965 if _CUSTOM_ATTRIBUTE.match(k): 966 custom[k] = details[k] 967 968 obj = Welcome(session, roles, realm, authid, authrole, authmethod, authprovider, authextra, resumed, resumable, resume_token, custom) 969 970 return obj 971 972 def marshal(self): 973 """ 974 Marshal this object into a raw message for subsequent serialization to bytes. 975 976 :returns: The serialized raw message. 977 :rtype: list 978 """ 979 details = {} 980 details.update(self.custom) 981 982 if self.realm: 983 details[u'realm'] = self.realm 984 985 if self.authid: 986 details[u'authid'] = self.authid 987 988 if self.authrole: 989 details[u'authrole'] = self.authrole 990 991 if self.authrole: 992 details[u'authmethod'] = self.authmethod 993 994 if self.authprovider: 995 details[u'authprovider'] = self.authprovider 996 997 if self.authextra: 998 details[u'authextra'] = self.authextra 999 1000 if self.resumed: 1001 details[u'resumed'] = self.resumed 1002 1003 if self.resumable: 1004 details[u'resumable'] = self.resumable 1005 1006 if self.resume_token: 1007 details[u'resume_token'] = self.resume_token 1008 1009 details[u'roles'] = {} 1010 for role in self.roles.values(): 1011 details[u'roles'][role.ROLE] = {} 1012 for feature in role.__dict__: 1013 if not feature.startswith('_') and feature != 'ROLE' and getattr(role, feature) is not None: 1014 if u'features' not in details[u'roles'][role.ROLE]: 1015 details[u'roles'][role.ROLE] = {u'features': {}} 1016 details[u'roles'][role.ROLE][u'features'][six.u(feature)] = getattr(role, feature) 1017 1018 return [Welcome.MESSAGE_TYPE, self.session, details] 1019 1020 def __str__(self): 1021 """ 1022 Returns string representation of this message. 1023 """ 1024 return u"Welcome(session={}, roles={}, realm={}, authid={}, authrole={}, authmethod={}, authprovider={}, authextra={}, resumed={}, resumable={}, resume_token={})".format(self.session, self.roles, self.realm, self.authid, self.authrole, self.authmethod, self.authprovider, self.authextra, self.resumed, self.resumable, self.resume_token) 1025 1026 1027class Abort(Message): 1028 """ 1029 A WAMP ``ABORT`` message. 1030 1031 Format: ``[ABORT, Details|dict, Reason|uri]`` 1032 """ 1033 1034 MESSAGE_TYPE = 3 1035 """ 1036 The WAMP message code for this type of message. 1037 """ 1038 1039 __slots__ = ( 1040 'reason', 1041 'message', 1042 ) 1043 1044 def __init__(self, reason, message=None): 1045 """ 1046 1047 :param reason: WAMP or application error URI for aborting reason. 1048 :type reason: str 1049 1050 :param message: Optional human-readable closing message, e.g. for logging purposes. 1051 :type message: str or None 1052 """ 1053 assert(type(reason) == six.text_type) 1054 assert(message is None or type(message) == six.text_type) 1055 1056 Message.__init__(self) 1057 self.reason = reason 1058 self.message = message 1059 1060 @staticmethod 1061 def parse(wmsg): 1062 """ 1063 Verifies and parses an unserialized raw message into an actual WAMP message instance. 1064 1065 :param wmsg: The unserialized raw message. 1066 :type wmsg: list 1067 1068 :returns: An instance of this class. 1069 """ 1070 # this should already be verified by WampSerializer.unserialize 1071 assert(len(wmsg) > 0 and wmsg[0] == Abort.MESSAGE_TYPE) 1072 1073 if len(wmsg) != 3: 1074 raise ProtocolError("invalid message length {0} for ABORT".format(len(wmsg))) 1075 1076 details = check_or_raise_extra(wmsg[1], u"'details' in ABORT") 1077 reason = check_or_raise_uri(wmsg[2], u"'reason' in ABORT") 1078 1079 message = None 1080 1081 if u'message' in details: 1082 1083 details_message = details[u'message'] 1084 if type(details_message) != six.text_type: 1085 raise ProtocolError("invalid type {0} for 'message' detail in ABORT".format(type(details_message))) 1086 1087 message = details_message 1088 1089 obj = Abort(reason, message) 1090 1091 return obj 1092 1093 def marshal(self): 1094 """ 1095 Marshal this object into a raw message for subsequent serialization to bytes. 1096 1097 :returns: The serialized raw message. 1098 :rtype: list 1099 """ 1100 details = {} 1101 if self.message: 1102 details[u'message'] = self.message 1103 1104 return [Abort.MESSAGE_TYPE, details, self.reason] 1105 1106 def __str__(self): 1107 """ 1108 Returns string representation of this message. 1109 """ 1110 return u"Abort(message={0}, reason={1})".format(self.message, self.reason) 1111 1112 1113class Challenge(Message): 1114 """ 1115 A WAMP ``CHALLENGE`` message. 1116 1117 Format: ``[CHALLENGE, Method|string, Extra|dict]`` 1118 """ 1119 1120 MESSAGE_TYPE = 4 1121 """ 1122 The WAMP message code for this type of message. 1123 """ 1124 1125 __slots__ = ( 1126 'method', 1127 'extra', 1128 ) 1129 1130 def __init__(self, method, extra=None): 1131 """ 1132 1133 :param method: The authentication method. 1134 :type method: str 1135 1136 :param extra: Authentication method specific information. 1137 :type extra: dict or None 1138 """ 1139 assert(type(method) == six.text_type) 1140 assert(extra is None or type(extra) == dict) 1141 1142 Message.__init__(self) 1143 self.method = method 1144 self.extra = extra or {} 1145 1146 @staticmethod 1147 def parse(wmsg): 1148 """ 1149 Verifies and parses an unserialized raw message into an actual WAMP message instance. 1150 1151 :param wmsg: The unserialized raw message. 1152 :type wmsg: list 1153 1154 :returns: An instance of this class. 1155 """ 1156 # this should already be verified by WampSerializer.unserialize 1157 assert(len(wmsg) > 0 and wmsg[0] == Challenge.MESSAGE_TYPE) 1158 1159 if len(wmsg) != 3: 1160 raise ProtocolError("invalid message length {0} for CHALLENGE".format(len(wmsg))) 1161 1162 method = wmsg[1] 1163 if type(method) != six.text_type: 1164 raise ProtocolError("invalid type {0} for 'method' in CHALLENGE".format(type(method))) 1165 1166 extra = check_or_raise_extra(wmsg[2], u"'extra' in CHALLENGE") 1167 1168 obj = Challenge(method, extra) 1169 1170 return obj 1171 1172 def marshal(self): 1173 """ 1174 Marshal this object into a raw message for subsequent serialization to bytes. 1175 1176 :returns: The serialized raw message. 1177 :rtype: list 1178 """ 1179 return [Challenge.MESSAGE_TYPE, self.method, self.extra] 1180 1181 def __str__(self): 1182 """ 1183 Returns string representation of this message. 1184 """ 1185 return u"Challenge(method={0}, extra={1})".format(self.method, self.extra) 1186 1187 1188class Authenticate(Message): 1189 """ 1190 A WAMP ``AUTHENTICATE`` message. 1191 1192 Format: ``[AUTHENTICATE, Signature|string, Extra|dict]`` 1193 """ 1194 1195 MESSAGE_TYPE = 5 1196 """ 1197 The WAMP message code for this type of message. 1198 """ 1199 1200 __slots__ = ( 1201 'signature', 1202 'extra', 1203 ) 1204 1205 def __init__(self, signature, extra=None): 1206 """ 1207 1208 :param signature: The signature for the authentication challenge. 1209 :type signature: str 1210 1211 :param extra: Authentication method specific information. 1212 :type extra: dict or None 1213 """ 1214 assert(type(signature) == six.text_type) 1215 assert(extra is None or type(extra) == dict) 1216 1217 Message.__init__(self) 1218 self.signature = signature 1219 self.extra = extra or {} 1220 1221 @staticmethod 1222 def parse(wmsg): 1223 """ 1224 Verifies and parses an unserialized raw message into an actual WAMP message instance. 1225 1226 :param wmsg: The unserialized raw message. 1227 :type wmsg: list 1228 1229 :returns: An instance of this class. 1230 """ 1231 # this should already be verified by WampSerializer.unserialize 1232 assert(len(wmsg) > 0 and wmsg[0] == Authenticate.MESSAGE_TYPE) 1233 1234 if len(wmsg) != 3: 1235 raise ProtocolError("invalid message length {0} for AUTHENTICATE".format(len(wmsg))) 1236 1237 signature = wmsg[1] 1238 if type(signature) != six.text_type: 1239 raise ProtocolError("invalid type {0} for 'signature' in AUTHENTICATE".format(type(signature))) 1240 1241 extra = check_or_raise_extra(wmsg[2], u"'extra' in AUTHENTICATE") 1242 1243 obj = Authenticate(signature, extra) 1244 1245 return obj 1246 1247 def marshal(self): 1248 """ 1249 Marshal this object into a raw message for subsequent serialization to bytes. 1250 1251 :returns: The serialized raw message. 1252 :rtype: list 1253 """ 1254 return [Authenticate.MESSAGE_TYPE, self.signature, self.extra] 1255 1256 def __str__(self): 1257 """ 1258 Returns string representation of this message. 1259 """ 1260 return u"Authenticate(signature={0}, extra={1})".format(self.signature, self.extra) 1261 1262 1263class Goodbye(Message): 1264 """ 1265 A WAMP ``GOODBYE`` message. 1266 1267 Format: ``[GOODBYE, Details|dict, Reason|uri]`` 1268 """ 1269 1270 MESSAGE_TYPE = 6 1271 """ 1272 The WAMP message code for this type of message. 1273 """ 1274 1275 DEFAULT_REASON = u"wamp.close.normal" 1276 """ 1277 Default WAMP closing reason. 1278 """ 1279 1280 __slots__ = ( 1281 'reason', 1282 'message', 1283 'resumable', 1284 ) 1285 1286 def __init__(self, reason=DEFAULT_REASON, message=None, resumable=None): 1287 """ 1288 1289 :param reason: Optional WAMP or application error URI for closing reason. 1290 :type reason: str 1291 1292 :param message: Optional human-readable closing message, e.g. for logging purposes. 1293 :type message: str or None 1294 1295 :param resumable: From the server: Whether the session is able to be resumed (true) or destroyed (false). From the client: Whether it should be resumable (true) or destroyed (false). 1296 :type resumable: bool or None 1297 """ 1298 assert(type(reason) == six.text_type) 1299 assert(message is None or type(message) == six.text_type) 1300 assert(resumable is None or type(resumable) == bool) 1301 1302 Message.__init__(self) 1303 self.reason = reason 1304 self.message = message 1305 self.resumable = resumable 1306 1307 @staticmethod 1308 def parse(wmsg): 1309 """ 1310 Verifies and parses an unserialized raw message into an actual WAMP message instance. 1311 1312 :param wmsg: The unserialized raw message. 1313 :type wmsg: list 1314 1315 :returns: An instance of this class. 1316 """ 1317 # this should already be verified by WampSerializer.unserialize 1318 assert(len(wmsg) > 0 and wmsg[0] == Goodbye.MESSAGE_TYPE) 1319 1320 if len(wmsg) != 3: 1321 raise ProtocolError("invalid message length {0} for GOODBYE".format(len(wmsg))) 1322 1323 details = check_or_raise_extra(wmsg[1], u"'details' in GOODBYE") 1324 reason = check_or_raise_uri(wmsg[2], u"'reason' in GOODBYE") 1325 1326 message = None 1327 resumable = None 1328 1329 if u'message' in details: 1330 1331 details_message = details[u'message'] 1332 if type(details_message) != six.text_type: 1333 raise ProtocolError("invalid type {0} for 'message' detail in GOODBYE".format(type(details_message))) 1334 1335 message = details_message 1336 1337 if u'resumable' in details: 1338 resumable = details[u'resumable'] 1339 if type(resumable) != bool: 1340 raise ProtocolError("invalid type {0} for 'resumable' detail in GOODBYE".format(type(resumable))) 1341 1342 obj = Goodbye(reason=reason, 1343 message=message, 1344 resumable=resumable) 1345 1346 return obj 1347 1348 def marshal(self): 1349 """ 1350 Marshal this object into a raw message for subsequent serialization to bytes. 1351 1352 :returns: The serialized raw message. 1353 :rtype: list 1354 """ 1355 details = {} 1356 if self.message: 1357 details[u'message'] = self.message 1358 1359 if self.resumable: 1360 details[u'resumable'] = self.resumable 1361 1362 return [Goodbye.MESSAGE_TYPE, details, self.reason] 1363 1364 def __str__(self): 1365 """ 1366 Returns string representation of this message. 1367 """ 1368 return u"Goodbye(message={}, reason={}, resumable={})".format(self.message, self.reason, self.resumable) 1369 1370 1371class Error(Message): 1372 """ 1373 A WAMP ``ERROR`` message. 1374 1375 Formats: 1376 1377 * ``[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri]`` 1378 * ``[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Arguments|list]`` 1379 * ``[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Arguments|list, ArgumentsKw|dict]`` 1380 * ``[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Payload|binary]`` 1381 """ 1382 1383 MESSAGE_TYPE = 8 1384 """ 1385 The WAMP message code for this type of message. 1386 """ 1387 1388 __slots__ = ( 1389 'request_type', 1390 'request', 1391 'error', 1392 'args', 1393 'kwargs', 1394 'payload', 1395 'enc_algo', 1396 'enc_key', 1397 'enc_serializer', 1398 'callee', 1399 'callee_authid', 1400 'callee_authrole', 1401 'forward_for', 1402 ) 1403 1404 def __init__(self, 1405 request_type, 1406 request, 1407 error, 1408 args=None, 1409 kwargs=None, 1410 payload=None, 1411 enc_algo=None, 1412 enc_key=None, 1413 enc_serializer=None, 1414 callee=None, 1415 callee_authid=None, 1416 callee_authrole=None, 1417 forward_for=None): 1418 """ 1419 1420 :param request_type: The WAMP message type code for the original request. 1421 :type request_type: int 1422 1423 :param request: The WAMP request ID of the original request (`Call`, `Subscribe`, ...) this error occurred for. 1424 :type request: int 1425 1426 :param error: The WAMP or application error URI for the error that occurred. 1427 :type error: str 1428 1429 :param args: Positional values for application-defined exception. 1430 Must be serializable using any serializers in use. 1431 :type args: list or None 1432 1433 :param kwargs: Keyword values for application-defined exception. 1434 Must be serializable using any serializers in use. 1435 :type kwargs: dict or None 1436 1437 :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset. 1438 :type payload: bytes or None 1439 1440 :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload. 1441 :type enc_algo: str or None 1442 1443 :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key. 1444 :type enc_key: str or None 1445 1446 :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload. 1447 :type enc_serializer: str or None 1448 1449 :param callee: The WAMP session ID of the effective callee that responded with the error. Only filled if callee is disclosed. 1450 :type callee: None or int 1451 1452 :param callee_authid: The WAMP authid of the responding callee. Only filled if callee is disclosed. 1453 :type callee_authid: None or unicode 1454 1455 :param callee_authrole: The WAMP authrole of the responding callee. Only filled if callee is disclosed. 1456 :type callee_authrole: None or unicode 1457 1458 :param forward_for: When this Error is forwarded for a client/callee (or from an intermediary router). 1459 :type forward_for: list[dict] 1460 """ 1461 assert(type(request_type) in six.integer_types) 1462 assert(type(request) in six.integer_types) 1463 assert(type(error) == six.text_type) 1464 assert(args is None or type(args) in [list, tuple]) 1465 assert(kwargs is None or type(kwargs) == dict) 1466 assert(payload is None or type(payload) == six.binary_type) 1467 assert(payload is None or (payload is not None and args is None and kwargs is None)) 1468 1469 assert(enc_algo is None or is_valid_enc_algo(enc_algo)) 1470 assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None)) 1471 assert(enc_key is None or type(enc_key) == six.text_type) 1472 assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer)) 1473 1474 assert(callee is None or type(callee) in six.integer_types) 1475 assert(callee_authid is None or type(callee_authid) == six.text_type) 1476 assert(callee_authrole is None or type(callee_authrole) == six.text_type) 1477 1478 assert(forward_for is None or type(forward_for) == list) 1479 if forward_for: 1480 for ff in forward_for: 1481 assert type(ff) == dict 1482 assert 'session' in ff and type(ff['session']) in six.integer_types 1483 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 1484 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 1485 1486 Message.__init__(self) 1487 self.request_type = request_type 1488 self.request = request 1489 self.error = error 1490 self.args = args 1491 self.kwargs = _validate_kwargs(kwargs) 1492 self.payload = payload 1493 1494 # payload transparency related knobs 1495 self.enc_algo = enc_algo 1496 self.enc_key = enc_key 1497 self.enc_serializer = enc_serializer 1498 1499 # effective callee that responded with the error 1500 self.callee = callee 1501 self.callee_authid = callee_authid 1502 self.callee_authrole = callee_authrole 1503 1504 # message forwarding 1505 self.forward_for = forward_for 1506 1507 @staticmethod 1508 def parse(wmsg): 1509 """ 1510 Verifies and parses an unserialized raw message into an actual WAMP message instance. 1511 1512 :param wmsg: The unserialized raw message. 1513 :type wmsg: list 1514 1515 :returns: An instance of this class. 1516 """ 1517 # this should already be verified by WampSerializer.unserialize 1518 assert(len(wmsg) > 0 and wmsg[0] == Error.MESSAGE_TYPE) 1519 1520 if len(wmsg) not in (5, 6, 7): 1521 raise ProtocolError("invalid message length {0} for ERROR".format(len(wmsg))) 1522 1523 request_type = wmsg[1] 1524 if type(request_type) not in six.integer_types: 1525 raise ProtocolError("invalid type {0} for 'request_type' in ERROR".format(request_type)) 1526 1527 if request_type not in [Subscribe.MESSAGE_TYPE, 1528 Unsubscribe.MESSAGE_TYPE, 1529 Publish.MESSAGE_TYPE, 1530 Register.MESSAGE_TYPE, 1531 Unregister.MESSAGE_TYPE, 1532 Call.MESSAGE_TYPE, 1533 Invocation.MESSAGE_TYPE]: 1534 raise ProtocolError("invalid value {0} for 'request_type' in ERROR".format(request_type)) 1535 1536 request = check_or_raise_id(wmsg[2], u"'request' in ERROR") 1537 details = check_or_raise_extra(wmsg[3], u"'details' in ERROR") 1538 error = check_or_raise_uri(wmsg[4], u"'error' in ERROR") 1539 1540 args = None 1541 kwargs = None 1542 payload = None 1543 enc_algo = None 1544 enc_key = None 1545 enc_serializer = None 1546 callee = None 1547 callee_authid = None 1548 callee_authrole = None 1549 forward_for = None 1550 1551 if len(wmsg) == 6 and type(wmsg[5]) == six.binary_type: 1552 1553 payload = wmsg[5] 1554 1555 enc_algo = details.get(u'enc_algo', None) 1556 if enc_algo and not is_valid_enc_algo(enc_algo): 1557 raise ProtocolError("invalid value {0} for 'enc_algo' detail in EVENT".format(enc_algo)) 1558 1559 enc_key = details.get(u'enc_key', None) 1560 if enc_key and type(enc_key) != six.text_type: 1561 raise ProtocolError("invalid type {0} for 'enc_key' detail in EVENT".format(type(enc_key))) 1562 1563 enc_serializer = details.get(u'enc_serializer', None) 1564 if enc_serializer and not is_valid_enc_serializer(enc_serializer): 1565 raise ProtocolError("invalid value {0} for 'enc_serializer' detail in EVENT".format(enc_serializer)) 1566 1567 else: 1568 if len(wmsg) > 5: 1569 args = wmsg[5] 1570 if args is not None and type(args) != list: 1571 raise ProtocolError("invalid type {0} for 'args' in ERROR".format(type(args))) 1572 1573 if len(wmsg) > 6: 1574 kwargs = wmsg[6] 1575 if type(kwargs) != dict: 1576 raise ProtocolError("invalid type {0} for 'kwargs' in ERROR".format(type(kwargs))) 1577 1578 if u'callee' in details: 1579 1580 detail_callee = details[u'callee'] 1581 if type(detail_callee) not in six.integer_types: 1582 raise ProtocolError("invalid type {0} for 'callee' detail in ERROR".format(type(detail_callee))) 1583 1584 callee = detail_callee 1585 1586 if u'callee_authid' in details: 1587 1588 detail_callee_authid = details[u'callee_authid'] 1589 if type(detail_callee_authid) != six.text_type: 1590 raise ProtocolError("invalid type {0} for 'callee_authid' detail in ERROR".format(type(detail_callee_authid))) 1591 1592 callee_authid = detail_callee_authid 1593 1594 if u'callee_authrole' in details: 1595 1596 detail_callee_authrole = details[u'callee_authrole'] 1597 if type(detail_callee_authrole) != six.text_type: 1598 raise ProtocolError("invalid type {0} for 'callee_authrole' detail in ERROR".format(type(detail_callee_authrole))) 1599 1600 callee_authrole = detail_callee_authrole 1601 1602 if u'forward_for' in details: 1603 forward_for = details[u'forward_for'] 1604 valid = False 1605 if type(forward_for) == list: 1606 for ff in forward_for: 1607 if type(ff) != dict: 1608 break 1609 if 'session' not in ff or type(ff['session']) not in six.integer_types: 1610 break 1611 if 'authid' not in ff or type(ff['authid']) != six.text_type: 1612 break 1613 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 1614 break 1615 valid = True 1616 1617 if not valid: 1618 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in ERROR") 1619 1620 obj = Error(request_type, 1621 request, 1622 error, 1623 args=args, 1624 kwargs=kwargs, 1625 payload=payload, 1626 enc_algo=enc_algo, 1627 enc_key=enc_key, 1628 enc_serializer=enc_serializer, 1629 callee=callee, 1630 callee_authid=callee_authid, 1631 callee_authrole=callee_authrole, 1632 forward_for=forward_for) 1633 1634 return obj 1635 1636 def marshal(self): 1637 """ 1638 Marshal this object into a raw message for subsequent serialization to bytes. 1639 1640 :returns: The serialized raw message. 1641 :rtype: list 1642 """ 1643 details = {} 1644 1645 if self.callee is not None: 1646 details[u'callee'] = self.callee 1647 if self.callee_authid is not None: 1648 details[u'callee_authid'] = self.callee_authid 1649 if self.callee_authrole is not None: 1650 details[u'callee_authrole'] = self.callee_authrole 1651 if self.forward_for is not None: 1652 details[u'forward_for'] = self.forward_for 1653 1654 if self.payload: 1655 if self.enc_algo is not None: 1656 details[u'enc_algo'] = self.enc_algo 1657 if self.enc_key is not None: 1658 details[u'enc_key'] = self.enc_key 1659 if self.enc_serializer is not None: 1660 details[u'enc_serializer'] = self.enc_serializer 1661 return [self.MESSAGE_TYPE, self.request_type, self.request, details, self.error, self.payload] 1662 else: 1663 if self.kwargs: 1664 return [self.MESSAGE_TYPE, self.request_type, self.request, details, self.error, self.args, self.kwargs] 1665 elif self.args: 1666 return [self.MESSAGE_TYPE, self.request_type, self.request, details, self.error, self.args] 1667 else: 1668 return [self.MESSAGE_TYPE, self.request_type, self.request, details, self.error] 1669 1670 def __str__(self): 1671 """ 1672 Returns string representation of this message. 1673 """ 1674 return u"Error(request_type={0}, request={1}, error={2}, args={3}, kwargs={4}, enc_algo={5}, enc_key={6}, enc_serializer={7}, payload={8}, callee={9}, callee_authid={10}, callee_authrole={11}, forward_for={12})".format(self.request_type, self.request, self.error, self.args, self.kwargs, self.enc_algo, self.enc_key, self.enc_serializer, b2a(self.payload), self.callee, self.callee_authid, self.callee_authrole, self.forward_for) 1675 1676 1677class Publish(Message): 1678 """ 1679 A WAMP ``PUBLISH`` message. 1680 1681 Formats: 1682 1683 * ``[PUBLISH, Request|id, Options|dict, Topic|uri]`` 1684 * ``[PUBLISH, Request|id, Options|dict, Topic|uri, Arguments|list]`` 1685 * ``[PUBLISH, Request|id, Options|dict, Topic|uri, Arguments|list, ArgumentsKw|dict]`` 1686 * ``[PUBLISH, Request|id, Options|dict, Topic|uri, Payload|binary]`` 1687 """ 1688 1689 MESSAGE_TYPE = 16 1690 """ 1691 The WAMP message code for this type of message. 1692 """ 1693 1694 __slots__ = ( 1695 # uint64 (key) 1696 '_request', 1697 1698 # string (required, uri) 1699 '_topic', 1700 1701 # [uint8] 1702 '_args', 1703 1704 # [uint8] 1705 '_kwargs', 1706 1707 # [uint8] 1708 '_payload', 1709 1710 # Payload => uint8 1711 '_enc_algo', 1712 1713 # Serializer => uint8 1714 '_enc_serializer', 1715 1716 # [uint8] 1717 '_enc_key', 1718 1719 # bool 1720 '_acknowledge', 1721 1722 # bool 1723 '_exclude_me', 1724 1725 # [uint64] 1726 '_exclude', 1727 1728 # [string] (principal) 1729 '_exclude_authid', 1730 1731 # [string] (principal) 1732 '_exclude_authrole', 1733 1734 # [uint64] 1735 '_eligible', 1736 1737 # [string] (principal) 1738 '_eligible_authid', 1739 1740 # [string] (principal) 1741 '_eligible_authrole', 1742 1743 # bool 1744 '_retain', 1745 1746 # [Principal] 1747 '_forward_for', 1748 ) 1749 1750 def __init__(self, 1751 request=None, 1752 topic=None, 1753 args=None, 1754 kwargs=None, 1755 payload=None, 1756 acknowledge=None, 1757 exclude_me=None, 1758 exclude=None, 1759 exclude_authid=None, 1760 exclude_authrole=None, 1761 eligible=None, 1762 eligible_authid=None, 1763 eligible_authrole=None, 1764 retain=None, 1765 enc_algo=None, 1766 enc_key=None, 1767 enc_serializer=None, 1768 forward_for=None, 1769 from_fbs=None): 1770 """ 1771 1772 :param request: The WAMP request ID of this request. 1773 :type request: int 1774 1775 :param topic: The WAMP or application URI of the PubSub topic the event should 1776 be published to. 1777 :type topic: str 1778 1779 :param args: Positional values for application-defined event payload. 1780 Must be serializable using any serializers in use. 1781 :type args: list or tuple or None 1782 1783 :param kwargs: Keyword values for application-defined event payload. 1784 Must be serializable using any serializers in use. 1785 :type kwargs: dict or None 1786 1787 :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset. 1788 :type payload: bytes or None 1789 1790 :param acknowledge: If True, acknowledge the publication with a success or 1791 error response. 1792 :type acknowledge: bool or None 1793 1794 :param exclude_me: If ``True``, exclude the publisher from receiving the event, even 1795 if he is subscribed (and eligible). 1796 :type exclude_me: bool or None 1797 1798 :param exclude: List of WAMP session IDs to exclude from receiving this event. 1799 :type exclude: list of int or None 1800 1801 :param exclude_authid: List of WAMP authids to exclude from receiving this event. 1802 :type exclude_authid: list of str or None 1803 1804 :param exclude_authrole: List of WAMP authroles to exclude from receiving this event. 1805 :type exclude_authrole: list of str or None 1806 1807 :param eligible: List of WAMP session IDs eligible to receive this event. 1808 :type eligible: list of int or None 1809 1810 :param eligible_authid: List of WAMP authids eligible to receive this event. 1811 :type eligible_authid: list of str or None 1812 1813 :param eligible_authrole: List of WAMP authroles eligible to receive this event. 1814 :type eligible_authrole: list of str or None 1815 1816 :param retain: If ``True``, request the broker retain this event. 1817 :type retain: bool or None 1818 1819 :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload. 1820 :type enc_algo: str or None 1821 1822 :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key. 1823 :type enc_key: str or None 1824 1825 :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload. 1826 :type enc_serializer: str or None or None 1827 1828 :param forward_for: When this Call is forwarded for a client (or from an intermediary router). 1829 :type forward_for: list[dict] 1830 """ 1831 assert(request is None or type(request) in six.integer_types) 1832 assert(topic is None or type(topic) == six.text_type) 1833 assert(args is None or type(args) in [list, tuple, six.text_type, six.binary_type]) 1834 assert(kwargs is None or type(kwargs) in [dict, six.text_type, six.binary_type]) 1835 assert(payload is None or type(payload) == six.binary_type) 1836 assert(payload is None or (payload is not None and args is None and kwargs is None)) 1837 assert(acknowledge is None or type(acknowledge) == bool) 1838 assert(retain is None or type(retain) == bool) 1839 1840 # publisher exlusion and black-/whitelisting 1841 assert(exclude_me is None or type(exclude_me) == bool) 1842 1843 assert(exclude is None or type(exclude) == list) 1844 if exclude: 1845 for sessionid in exclude: 1846 assert(type(sessionid) in six.integer_types) 1847 1848 assert(exclude_authid is None or type(exclude_authid) == list) 1849 if exclude_authid: 1850 for authid in exclude_authid: 1851 assert(type(authid) == six.text_type) 1852 1853 assert(exclude_authrole is None or type(exclude_authrole) == list) 1854 if exclude_authrole: 1855 for authrole in exclude_authrole: 1856 assert(type(authrole) == six.text_type) 1857 1858 assert(eligible is None or type(eligible) == list) 1859 if eligible: 1860 for sessionid in eligible: 1861 assert(type(sessionid) in six.integer_types) 1862 1863 assert(eligible_authid is None or type(eligible_authid) == list) 1864 if eligible_authid: 1865 for authid in eligible_authid: 1866 assert(type(authid) == six.text_type) 1867 1868 assert(eligible_authrole is None or type(eligible_authrole) == list) 1869 if eligible_authrole: 1870 for authrole in eligible_authrole: 1871 assert(type(authrole) == six.text_type) 1872 1873 assert(enc_algo is None or is_valid_enc_algo(enc_algo)) 1874 assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None)) 1875 assert(enc_key is None or type(enc_key) == six.text_type) 1876 assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer)) 1877 1878 assert(forward_for is None or type(forward_for) == list) 1879 if forward_for: 1880 for ff in forward_for: 1881 assert type(ff) == dict 1882 assert 'session' in ff and type(ff['session']) in six.integer_types 1883 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 1884 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 1885 1886 Message.__init__(self, from_fbs=from_fbs) 1887 self._request = request 1888 self._topic = topic 1889 self._args = args 1890 self._kwargs = _validate_kwargs(kwargs) 1891 self._payload = payload 1892 self._acknowledge = acknowledge 1893 1894 # publisher exlusion and black-/whitelisting 1895 self._exclude_me = exclude_me 1896 self._exclude = exclude 1897 self._exclude_authid = exclude_authid 1898 self._exclude_authrole = exclude_authrole 1899 self._eligible = eligible 1900 self._eligible_authid = eligible_authid 1901 self._eligible_authrole = eligible_authrole 1902 1903 # event retention 1904 self._retain = retain 1905 1906 # payload transparency related knobs 1907 self._enc_algo = enc_algo 1908 self._enc_key = enc_key 1909 self._enc_serializer = enc_serializer 1910 1911 # message forwarding 1912 self._forward_for = forward_for 1913 1914 def __eq__(self, other): 1915 if not isinstance(other, self.__class__): 1916 return False 1917 if not Message.__eq__(self, other): 1918 return False 1919 if other.request != self.request: 1920 return False 1921 if other.topic != self.topic: 1922 return False 1923 if other.args != self.args: 1924 return False 1925 if other.kwargs != self.kwargs: 1926 return False 1927 if other.payload != self.payload: 1928 return False 1929 if other.acknowledge != self.acknowledge: 1930 return False 1931 if other.exclude_me != self.exclude_me: 1932 return False 1933 if other.exclude != self.exclude: 1934 return False 1935 if other.exclude_authid != self.exclude_authid: 1936 return False 1937 if other.exclude_authrole != self.exclude_authrole: 1938 return False 1939 if other.eligible != self.eligible: 1940 return False 1941 if other.eligible_authid != self.eligible_authid: 1942 return False 1943 if other.eligible_authrole != self.eligible_authrole: 1944 return False 1945 if other.retain != self.retain: 1946 return False 1947 if other.enc_algo != self.enc_algo: 1948 return False 1949 if other.enc_key != self.enc_key: 1950 return False 1951 if other.enc_serializer != self.enc_serializer: 1952 return False 1953 if other.forward_for != self.forward_for: 1954 return False 1955 return True 1956 1957 def __ne__(self, other): 1958 return not self.__eq__(other) 1959 1960 @property 1961 def request(self): 1962 if self._request is None and self._from_fbs: 1963 self._request = self._from_fbs.Request() 1964 return self._request 1965 1966 @request.setter 1967 def request(self, value): 1968 assert(value is None or type(value) in six.integer_types) 1969 self._request = value 1970 1971 @property 1972 def topic(self): 1973 if self._topic is None and self._from_fbs: 1974 s = self._from_fbs.Topic() 1975 if s: 1976 self._topic = s.decode('utf8') 1977 return self._topic 1978 1979 @topic.setter 1980 def topic(self, value): 1981 assert value is None or type(value) == str 1982 self._topic = value 1983 1984 @property 1985 def args(self): 1986 if self._args is None and self._from_fbs: 1987 if self._from_fbs.ArgsLength(): 1988 self._args = cbor.loads(bytes(self._from_fbs.ArgsAsBytes())) 1989 return self._args 1990 1991 @args.setter 1992 def args(self, value): 1993 assert(value is None or type(value) in [list, tuple]) 1994 self._args = value 1995 1996 @property 1997 def kwargs(self): 1998 if self._kwargs is None and self._from_fbs: 1999 if self._from_fbs.KwargsLength(): 2000 self._kwargs = cbor.loads(bytes(self._from_fbs.KwargsAsBytes())) 2001 return self._kwargs 2002 2003 @kwargs.setter 2004 def kwargs(self, value): 2005 assert(value is None or type(value) == dict) 2006 self._kwargs = value 2007 2008 @property 2009 def payload(self): 2010 if self._payload is None and self._from_fbs: 2011 if self._from_fbs.PayloadLength(): 2012 self._payload = self._from_fbs.PayloadAsBytes() 2013 return self._payload 2014 2015 @payload.setter 2016 def payload(self, value): 2017 assert value is None or type(value) == bytes 2018 self._payload = value 2019 2020 @property 2021 def acknowledge(self): 2022 if self._acknowledge is None and self._from_fbs: 2023 acknowledge = self._from_fbs.Acknowledge() 2024 if acknowledge: 2025 self._acknowledge = acknowledge 2026 return self._acknowledge 2027 2028 @acknowledge.setter 2029 def acknowledge(self, value): 2030 assert value is None or type(value) == bool 2031 self._acknowledge = value 2032 2033 @property 2034 def exclude_me(self): 2035 if self._exclude_me is None and self._from_fbs: 2036 exclude_me = self._from_fbs.ExcludeMe() 2037 if exclude_me is False: 2038 self._exclude_me = exclude_me 2039 return self._exclude_me 2040 2041 @exclude_me.setter 2042 def exclude_me(self, value): 2043 assert value is None or type(value) == bool 2044 self._exclude_me = value 2045 2046 @property 2047 def exclude(self): 2048 if self._exclude is None and self._from_fbs: 2049 if self._from_fbs.ExcludeLength(): 2050 exclude = [] 2051 for j in range(self._from_fbs.ExcludeLength()): 2052 exclude.append(self._from_fbs.Exclude(j)) 2053 self._exclude = exclude 2054 return self._exclude 2055 2056 @exclude.setter 2057 def exclude(self, value): 2058 assert value is None or type(value) == list 2059 if value: 2060 for x in value: 2061 assert type(x) == int 2062 self._exclude = value 2063 2064 @property 2065 def exclude_authid(self): 2066 if self._exclude_authid is None and self._from_fbs: 2067 if self._from_fbs.ExcludeAuthidLength(): 2068 exclude_authid = [] 2069 for j in range(self._from_fbs.ExcludeAuthidLength()): 2070 exclude_authid.append(self._from_fbs.ExcludeAuthid(j).decode('utf8')) 2071 self._exclude_authid = exclude_authid 2072 return self._exclude_authid 2073 2074 @exclude_authid.setter 2075 def exclude_authid(self, value): 2076 assert value is None or type(value) == list 2077 if value: 2078 for x in value: 2079 assert type(x) == str 2080 self._exclude_authid = value 2081 2082 @property 2083 def exclude_authrole(self): 2084 if self._exclude_authrole is None and self._from_fbs: 2085 if self._from_fbs.ExcludeAuthroleLength(): 2086 exclude_authrole = [] 2087 for j in range(self._from_fbs.ExcludeAuthroleLength()): 2088 exclude_authrole.append(self._from_fbs.ExcludeAuthrole(j).decode('utf8')) 2089 self._exclude_authrole = exclude_authrole 2090 return self._exclude_authrole 2091 2092 @exclude_authrole.setter 2093 def exclude_authrole(self, value): 2094 assert value is None or type(value) == list 2095 if value: 2096 for x in value: 2097 assert type(x) == str 2098 self._exclude_authrole = value 2099 2100 @property 2101 def eligible(self): 2102 if self._eligible is None and self._from_fbs: 2103 if self._from_fbs.EligibleLength(): 2104 eligible = [] 2105 for j in range(self._from_fbs.EligibleLength()): 2106 eligible.append(self._from_fbs.Eligible(j)) 2107 self._eligible = eligible 2108 return self._eligible 2109 2110 @eligible.setter 2111 def eligible(self, value): 2112 assert value is None or type(value) == list 2113 if value: 2114 for x in value: 2115 assert type(x) == int 2116 self._eligible = value 2117 2118 @property 2119 def eligible_authid(self): 2120 if self._eligible_authid is None and self._from_fbs: 2121 if self._from_fbs.EligibleAuthidLength(): 2122 eligible_authid = [] 2123 for j in range(self._from_fbs.EligibleAuthidLength()): 2124 eligible_authid.append(self._from_fbs.EligibleAuthid(j).decode('utf8')) 2125 self._eligible_authid = eligible_authid 2126 return self._eligible_authid 2127 2128 @eligible_authid.setter 2129 def eligible_authid(self, value): 2130 assert value is None or type(value) == list 2131 if value: 2132 for x in value: 2133 assert type(x) == str 2134 self._eligible_authid = value 2135 2136 @property 2137 def eligible_authrole(self): 2138 if self._eligible_authrole is None and self._from_fbs: 2139 if self._from_fbs.EligibleAuthroleLength(): 2140 eligible_authrole = [] 2141 for j in range(self._from_fbs.EligibleAuthroleLength()): 2142 eligible_authrole.append(self._from_fbs.EligibleAuthrole(j).decode('utf8')) 2143 self._eligible_authrole = eligible_authrole 2144 return self._eligible_authrole 2145 2146 @eligible_authrole.setter 2147 def eligible_authrole(self, value): 2148 assert value is None or type(value) == list 2149 if value: 2150 for x in value: 2151 assert type(x) == str 2152 self._eligible_authrole = value 2153 2154 @property 2155 def retain(self): 2156 if self._retain is None and self._from_fbs: 2157 retain = self._from_fbs.Retain() 2158 if retain: 2159 self._retain = retain 2160 return self._retain 2161 2162 @retain.setter 2163 def retain(self, value): 2164 assert value is None or type(value) == bool 2165 self._retain = value 2166 2167 @property 2168 def enc_algo(self): 2169 if self._enc_algo is None and self._from_fbs: 2170 enc_algo = self._from_fbs.EncAlgo() 2171 if enc_algo: 2172 self._enc_algo = enc_algo 2173 return self._enc_algo 2174 2175 @enc_algo.setter 2176 def enc_algo(self, value): 2177 assert value is None or value in [ENC_ALGO_CRYPTOBOX, ENC_ALGO_MQTT, ENC_ALGO_XBR] 2178 self._enc_algo = value 2179 2180 @property 2181 def enc_key(self): 2182 if self._enc_key is None and self._from_fbs: 2183 if self._from_fbs.EncKeyLength(): 2184 self._enc_key = self._from_fbs.EncKeyAsBytes() 2185 return self._enc_key 2186 2187 @enc_key.setter 2188 def enc_key(self, value): 2189 assert value is None or type(value) == bytes 2190 self._enc_key = value 2191 2192 @property 2193 def enc_serializer(self): 2194 if self._enc_serializer is None and self._from_fbs: 2195 enc_serializer = self._from_fbs.EncSerializer() 2196 if enc_serializer: 2197 self._enc_serializer = enc_serializer 2198 return self._enc_serializer 2199 2200 @enc_serializer.setter 2201 def enc_serializer(self, value): 2202 assert value is None or value in [ENC_SER_JSON, ENC_SER_MSGPACK, ENC_SER_CBOR, ENC_SER_UBJSON] 2203 self._enc_serializer = value 2204 2205 @property 2206 def forward_for(self): 2207 # FIXME 2208 return self._forward_for 2209 2210 @forward_for.setter 2211 def forward_for(self, value): 2212 # FIXME 2213 self._forward_for = value 2214 2215 @staticmethod 2216 def cast(buf): 2217 return Publish(from_fbs=message_fbs.Publish.GetRootAsPublish(buf, 0)) 2218 2219 def build(self, builder): 2220 2221 args = self.args 2222 if args: 2223 args = builder.CreateByteVector(cbor.dumps(args)) 2224 2225 kwargs = self.kwargs 2226 if kwargs: 2227 kwargs = builder.CreateByteVector(cbor.dumps(kwargs)) 2228 2229 payload = self.payload 2230 if payload: 2231 payload = builder.CreateByteVector(payload) 2232 2233 topic = self.topic 2234 if topic: 2235 topic = builder.CreateString(topic) 2236 2237 enc_key = self.enc_key 2238 if enc_key: 2239 enc_key = builder.CreateByteVector(enc_key) 2240 2241 # exclude: [int] 2242 exclude = self.exclude 2243 if exclude: 2244 message_fbs.PublishGen.PublishStartExcludeAuthidVector(builder, len(exclude)) 2245 for session in reversed(exclude): 2246 builder.PrependUint64(session) 2247 exclude = builder.EndVector(len(exclude)) 2248 2249 # exclude_authid: [string] 2250 exclude_authid = self.exclude_authid 2251 if exclude_authid: 2252 _exclude_authid = [] 2253 for authid in exclude_authid: 2254 _exclude_authid.append(builder.CreateString(authid)) 2255 message_fbs.PublishGen.PublishStartExcludeAuthidVector(builder, len(_exclude_authid)) 2256 for o in reversed(_exclude_authid): 2257 builder.PrependUOffsetTRelative(o) 2258 exclude_authid = builder.EndVector(len(_exclude_authid)) 2259 2260 # exclude_authrole: [string] 2261 exclude_authrole = self.exclude_authrole 2262 if exclude_authid: 2263 _exclude_authrole = [] 2264 for authrole in exclude_authrole: 2265 _exclude_authrole.append(builder.CreateString(authrole)) 2266 message_fbs.PublishGen.PublishStartExcludeAuthroleVector(builder, len(_exclude_authrole)) 2267 for o in reversed(_exclude_authrole): 2268 builder.PrependUOffsetTRelative(o) 2269 exclude_authrole = builder.EndVector(len(_exclude_authrole)) 2270 2271 # eligible: [int] 2272 eligible = self.eligible 2273 if eligible: 2274 message_fbs.PublishGen.PublishStartEligibleAuthidVector(builder, len(eligible)) 2275 for session in reversed(eligible): 2276 builder.PrependUint64(session) 2277 eligible = builder.EndVector(len(eligible)) 2278 2279 # eligible_authid: [string] 2280 eligible_authid = self.eligible_authid 2281 if eligible_authid: 2282 _eligible_authid = [] 2283 for authid in eligible_authid: 2284 _eligible_authid.append(builder.CreateString(authid)) 2285 message_fbs.PublishGen.PublishStartEligibleAuthidVector(builder, len(_eligible_authid)) 2286 for o in reversed(_eligible_authid): 2287 builder.PrependUOffsetTRelative(o) 2288 eligible_authid = builder.EndVector(len(_eligible_authid)) 2289 2290 # eligible_authrole: [string] 2291 eligible_authrole = self.eligible_authrole 2292 if eligible_authrole: 2293 _eligible_authrole = [] 2294 for authrole in eligible_authrole: 2295 _eligible_authrole.append(builder.CreateString(authrole)) 2296 message_fbs.PublishGen.PublishStartEligibleAuthroleVector(builder, len(_eligible_authrole)) 2297 for o in reversed(_eligible_authrole): 2298 builder.PrependUOffsetTRelative(o) 2299 eligible_authrole = builder.EndVector(len(_eligible_authrole)) 2300 2301 # now start and build a new object .. 2302 message_fbs.PublishGen.PublishStart(builder) 2303 2304 if self.request is not None: 2305 message_fbs.PublishGen.PublishAddRequest(builder, self.request) 2306 2307 if topic: 2308 message_fbs.PublishGen.PublishAddTopic(builder, topic) 2309 2310 if args: 2311 message_fbs.PublishGen.PublishAddArgs(builder, args) 2312 if kwargs: 2313 message_fbs.PublishGen.PublishAddKwargs(builder, kwargs) 2314 if payload: 2315 message_fbs.PublishGen.PublishAddPayload(builder, payload) 2316 2317 if self.acknowledge is not None: 2318 message_fbs.PublishGen.PublishAddAcknowledge(builder, self.acknowledge) 2319 if self.retain is not None: 2320 message_fbs.PublishGen.PublishAddRetain(builder, self.retain) 2321 if self.exclude_me is not None: 2322 message_fbs.PublishGen.PublishAddExcludeMe(builder, self.exclude_me) 2323 2324 if exclude: 2325 message_fbs.PublishGen.PublishAddExclude(builder, exclude) 2326 if exclude_authid: 2327 message_fbs.PublishGen.PublishAddExcludeAuthid(builder, exclude_authid) 2328 if exclude_authrole: 2329 message_fbs.PublishGen.PublishAddExcludeAuthrole(builder, exclude_authrole) 2330 2331 if eligible: 2332 message_fbs.PublishGen.PublishAddEligible(builder, eligible) 2333 if eligible_authid: 2334 message_fbs.PublishGen.PublishAddEligibleAuthid(builder, eligible_authid) 2335 if eligible_authrole: 2336 message_fbs.PublishGen.PublishAddEligibleAuthrole(builder, eligible_authrole) 2337 2338 if self.enc_algo: 2339 message_fbs.PublishGen.PublishAddEncAlgo(builder, self.enc_algo) 2340 if enc_key: 2341 message_fbs.PublishGen.PublishAddEncKey(builder, enc_key) 2342 if self.enc_serializer: 2343 message_fbs.PublishGen.PublishAddEncSerializer(builder, self.enc_serializer) 2344 2345 # FIXME: add forward_for 2346 2347 msg = message_fbs.PublishGen.PublishEnd(builder) 2348 2349 message_fbs.Message.MessageStart(builder) 2350 message_fbs.Message.MessageAddMsgType(builder, message_fbs.MessageType.PUBLISH) 2351 message_fbs.Message.MessageAddMsg(builder, msg) 2352 union_msg = message_fbs.Message.MessageEnd(builder) 2353 2354 return union_msg 2355 2356 @staticmethod 2357 def parse(wmsg): 2358 """ 2359 Verifies and parses an unserialized raw message into an actual WAMP message instance. 2360 2361 :param wmsg: The unserialized raw message. 2362 :type wmsg: list 2363 2364 :returns: An instance of this class. 2365 """ 2366 # this should already be verified by WampSerializer.unserialize 2367 assert(len(wmsg) > 0 and wmsg[0] == Publish.MESSAGE_TYPE) 2368 2369 if len(wmsg) not in (4, 5, 6): 2370 raise ProtocolError("invalid message length {0} for PUBLISH".format(len(wmsg))) 2371 2372 request = check_or_raise_id(wmsg[1], u"'request' in PUBLISH") 2373 options = check_or_raise_extra(wmsg[2], u"'options' in PUBLISH") 2374 topic = check_or_raise_uri(wmsg[3], u"'topic' in PUBLISH") 2375 2376 args = None 2377 kwargs = None 2378 payload = None 2379 2380 if len(wmsg) == 5 and type(wmsg[4]) in [six.text_type, six.binary_type]: 2381 2382 payload = wmsg[4] 2383 2384 enc_algo = options.get(u'enc_algo', None) 2385 if enc_algo and not is_valid_enc_algo(enc_algo): 2386 raise ProtocolError("invalid value {0} for 'enc_algo' option in PUBLISH".format(enc_algo)) 2387 2388 enc_key = options.get(u'enc_key', None) 2389 if enc_key and type(enc_key) != six.text_type: 2390 raise ProtocolError("invalid type {0} for 'enc_key' option in PUBLISH".format(type(enc_key))) 2391 2392 enc_serializer = options.get(u'enc_serializer', None) 2393 if enc_serializer and not is_valid_enc_serializer(enc_serializer): 2394 raise ProtocolError("invalid value {0} for 'enc_serializer' option in PUBLISH".format(enc_serializer)) 2395 2396 else: 2397 if len(wmsg) > 4: 2398 args = wmsg[4] 2399 if type(args) not in [list, six.text_type, six.binary_type]: 2400 raise ProtocolError("invalid type {0} for 'args' in PUBLISH".format(type(args))) 2401 2402 if len(wmsg) > 5: 2403 kwargs = wmsg[5] 2404 if type(kwargs) not in [dict, six.text_type, six.binary_type]: 2405 raise ProtocolError("invalid type {0} for 'kwargs' in PUBLISH".format(type(kwargs))) 2406 2407 enc_algo = None 2408 enc_key = None 2409 enc_serializer = None 2410 2411 acknowledge = None 2412 exclude_me = None 2413 exclude = None 2414 exclude_authid = None 2415 exclude_authrole = None 2416 eligible = None 2417 eligible_authid = None 2418 eligible_authrole = None 2419 retain = None 2420 forward_for = None 2421 2422 if u'acknowledge' in options: 2423 2424 option_acknowledge = options[u'acknowledge'] 2425 if type(option_acknowledge) != bool: 2426 raise ProtocolError("invalid type {0} for 'acknowledge' option in PUBLISH".format(type(option_acknowledge))) 2427 2428 acknowledge = option_acknowledge 2429 2430 if u'exclude_me' in options: 2431 2432 option_exclude_me = options[u'exclude_me'] 2433 if type(option_exclude_me) != bool: 2434 raise ProtocolError("invalid type {0} for 'exclude_me' option in PUBLISH".format(type(option_exclude_me))) 2435 2436 exclude_me = option_exclude_me 2437 2438 if u'exclude' in options: 2439 2440 option_exclude = options[u'exclude'] 2441 if type(option_exclude) != list: 2442 raise ProtocolError("invalid type {0} for 'exclude' option in PUBLISH".format(type(option_exclude))) 2443 2444 for _sessionid in option_exclude: 2445 if type(_sessionid) not in six.integer_types: 2446 raise ProtocolError("invalid type {0} for value in 'exclude' option in PUBLISH".format(type(_sessionid))) 2447 2448 exclude = option_exclude 2449 2450 if u'exclude_authid' in options: 2451 2452 option_exclude_authid = options[u'exclude_authid'] 2453 if type(option_exclude_authid) != list: 2454 raise ProtocolError("invalid type {0} for 'exclude_authid' option in PUBLISH".format(type(option_exclude_authid))) 2455 2456 for _authid in option_exclude_authid: 2457 if type(_authid) != six.text_type: 2458 raise ProtocolError("invalid type {0} for value in 'exclude_authid' option in PUBLISH".format(type(_authid))) 2459 2460 exclude_authid = option_exclude_authid 2461 2462 if u'exclude_authrole' in options: 2463 2464 option_exclude_authrole = options[u'exclude_authrole'] 2465 if type(option_exclude_authrole) != list: 2466 raise ProtocolError("invalid type {0} for 'exclude_authrole' option in PUBLISH".format(type(option_exclude_authrole))) 2467 2468 for _authrole in option_exclude_authrole: 2469 if type(_authrole) != six.text_type: 2470 raise ProtocolError("invalid type {0} for value in 'exclude_authrole' option in PUBLISH".format(type(_authrole))) 2471 2472 exclude_authrole = option_exclude_authrole 2473 2474 if u'eligible' in options: 2475 2476 option_eligible = options[u'eligible'] 2477 if type(option_eligible) != list: 2478 raise ProtocolError("invalid type {0} for 'eligible' option in PUBLISH".format(type(option_eligible))) 2479 2480 for sessionId in option_eligible: 2481 if type(sessionId) not in six.integer_types: 2482 raise ProtocolError("invalid type {0} for value in 'eligible' option in PUBLISH".format(type(sessionId))) 2483 2484 eligible = option_eligible 2485 2486 if u'eligible_authid' in options: 2487 2488 option_eligible_authid = options[u'eligible_authid'] 2489 if type(option_eligible_authid) != list: 2490 raise ProtocolError("invalid type {0} for 'eligible_authid' option in PUBLISH".format(type(option_eligible_authid))) 2491 2492 for _authid in option_eligible_authid: 2493 if type(_authid) != six.text_type: 2494 raise ProtocolError("invalid type {0} for value in 'eligible_authid' option in PUBLISH".format(type(_authid))) 2495 2496 eligible_authid = option_eligible_authid 2497 2498 if u'eligible_authrole' in options: 2499 2500 option_eligible_authrole = options[u'eligible_authrole'] 2501 if type(option_eligible_authrole) != list: 2502 raise ProtocolError("invalid type {0} for 'eligible_authrole' option in PUBLISH".format(type(option_eligible_authrole))) 2503 2504 for _authrole in option_eligible_authrole: 2505 if type(_authrole) != six.text_type: 2506 raise ProtocolError("invalid type {0} for value in 'eligible_authrole' option in PUBLISH".format(type(_authrole))) 2507 2508 eligible_authrole = option_eligible_authrole 2509 2510 if u'retain' in options: 2511 retain = options[u'retain'] 2512 if type(retain) != bool: 2513 raise ProtocolError("invalid type {0} for 'retain' option in PUBLISH".format(type(retain))) 2514 2515 if u'forward_for' in options: 2516 forward_for = options[u'forward_for'] 2517 valid = False 2518 if type(forward_for) == list: 2519 for ff in forward_for: 2520 if type(ff) != dict: 2521 break 2522 if 'session' not in ff or type(ff['session']) not in six.integer_types: 2523 break 2524 if 'authid' not in ff or type(ff['authid']) != six.text_type: 2525 break 2526 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 2527 break 2528 valid = True 2529 2530 if not valid: 2531 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in PUBLISH") 2532 2533 obj = Publish(request, 2534 topic, 2535 args=args, 2536 kwargs=kwargs, 2537 payload=payload, 2538 acknowledge=acknowledge, 2539 exclude_me=exclude_me, 2540 exclude=exclude, 2541 exclude_authid=exclude_authid, 2542 exclude_authrole=exclude_authrole, 2543 eligible=eligible, 2544 eligible_authid=eligible_authid, 2545 eligible_authrole=eligible_authrole, 2546 retain=retain, 2547 enc_algo=enc_algo, 2548 enc_key=enc_key, 2549 enc_serializer=enc_serializer, 2550 forward_for=forward_for) 2551 2552 return obj 2553 2554 def marshal_options(self): 2555 options = {} 2556 2557 if self.acknowledge is not None: 2558 options[u'acknowledge'] = self.acknowledge 2559 2560 if self.exclude_me is not None: 2561 options[u'exclude_me'] = self.exclude_me 2562 if self.exclude is not None: 2563 options[u'exclude'] = self.exclude 2564 if self.exclude_authid is not None: 2565 options[u'exclude_authid'] = self.exclude_authid 2566 if self.exclude_authrole is not None: 2567 options[u'exclude_authrole'] = self.exclude_authrole 2568 if self.eligible is not None: 2569 options[u'eligible'] = self.eligible 2570 if self.eligible_authid is not None: 2571 options[u'eligible_authid'] = self.eligible_authid 2572 if self.eligible_authrole is not None: 2573 options[u'eligible_authrole'] = self.eligible_authrole 2574 if self.retain is not None: 2575 options[u'retain'] = self.retain 2576 2577 if self.payload: 2578 if self.enc_algo is not None: 2579 options[u'enc_algo'] = self.enc_algo 2580 if self.enc_key is not None: 2581 options[u'enc_key'] = self.enc_key 2582 if self.enc_serializer is not None: 2583 options[u'enc_serializer'] = self.enc_serializer 2584 2585 if self.forward_for is not None: 2586 options[u'forward_for'] = self.forward_for 2587 2588 return options 2589 2590 def marshal(self): 2591 """ 2592 Marshal this object into a raw message for subsequent serialization to bytes. 2593 2594 :returns: The serialized raw message. 2595 :rtype: list 2596 """ 2597 options = self.marshal_options() 2598 2599 if self.payload: 2600 return [Publish.MESSAGE_TYPE, self.request, options, self.topic, self.payload] 2601 else: 2602 if self.kwargs: 2603 return [Publish.MESSAGE_TYPE, self.request, options, self.topic, self.args, self.kwargs] 2604 elif self.args: 2605 return [Publish.MESSAGE_TYPE, self.request, options, self.topic, self.args] 2606 else: 2607 return [Publish.MESSAGE_TYPE, self.request, options, self.topic] 2608 2609 def __str__(self): 2610 """ 2611 Returns string representation of this message. 2612 """ 2613 return u"Publish(request={}, topic={}, args={}, kwargs={}, acknowledge={}, exclude_me={}, exclude={}, exclude_authid={}, exclude_authrole={}, eligible={}, eligible_authid={}, eligible_authrole={}, retain={}, enc_algo={}, enc_key={}, enc_serializer={}, payload={}, forward_for={})".format(self.request, self.topic, self.args, self.kwargs, self.acknowledge, self.exclude_me, self.exclude, self.exclude_authid, self.exclude_authrole, self.eligible, self.eligible_authid, self.eligible_authrole, self.retain, self.enc_algo, self.enc_key, self.enc_serializer, b2a(self.payload), self.forward_for) 2614 2615 2616class Published(Message): 2617 """ 2618 A WAMP ``PUBLISHED`` message. 2619 2620 Format: ``[PUBLISHED, PUBLISH.Request|id, Publication|id]`` 2621 """ 2622 2623 MESSAGE_TYPE = 17 2624 """ 2625 The WAMP message code for this type of message. 2626 """ 2627 2628 __slots__ = ( 2629 'request', 2630 'publication', 2631 ) 2632 2633 def __init__(self, request, publication): 2634 """ 2635 2636 :param request: The request ID of the original `PUBLISH` request. 2637 :type request: int 2638 2639 :param publication: The publication ID for the published event. 2640 :type publication: int 2641 """ 2642 assert(type(request) in six.integer_types) 2643 assert(type(publication) in six.integer_types) 2644 2645 Message.__init__(self) 2646 self.request = request 2647 self.publication = publication 2648 2649 @staticmethod 2650 def parse(wmsg): 2651 """ 2652 Verifies and parses an unserialized raw message into an actual WAMP message instance. 2653 2654 :param wmsg: The unserialized raw message. 2655 :type wmsg: list 2656 2657 :returns: An instance of this class. 2658 """ 2659 # this should already be verified by WampSerializer.unserialize 2660 assert(len(wmsg) > 0 and wmsg[0] == Published.MESSAGE_TYPE) 2661 2662 if len(wmsg) != 3: 2663 raise ProtocolError("invalid message length {0} for PUBLISHED".format(len(wmsg))) 2664 2665 request = check_or_raise_id(wmsg[1], u"'request' in PUBLISHED") 2666 publication = check_or_raise_id(wmsg[2], u"'publication' in PUBLISHED") 2667 2668 obj = Published(request, publication) 2669 2670 return obj 2671 2672 def marshal(self): 2673 """ 2674 Marshal this object into a raw message for subsequent serialization to bytes. 2675 2676 :returns: The serialized raw message. 2677 :rtype: list 2678 """ 2679 return [Published.MESSAGE_TYPE, self.request, self.publication] 2680 2681 def __str__(self): 2682 """ 2683 Returns string representation of this message. 2684 """ 2685 return u"Published(request={0}, publication={1})".format(self.request, self.publication) 2686 2687 2688class Subscribe(Message): 2689 """ 2690 A WAMP ``SUBSCRIBE`` message. 2691 2692 Format: ``[SUBSCRIBE, Request|id, Options|dict, Topic|uri]`` 2693 """ 2694 2695 MESSAGE_TYPE = 32 2696 """ 2697 The WAMP message code for this type of message. 2698 """ 2699 2700 MATCH_EXACT = u'exact' 2701 MATCH_PREFIX = u'prefix' 2702 MATCH_WILDCARD = u'wildcard' 2703 2704 __slots__ = ( 2705 'request', 2706 'topic', 2707 'match', 2708 'get_retained', 2709 'forward_for', 2710 ) 2711 2712 def __init__(self, 2713 request, 2714 topic, 2715 match=None, 2716 get_retained=None, 2717 forward_for=None): 2718 """ 2719 2720 :param request: The WAMP request ID of this request. 2721 :type request: int 2722 2723 :param topic: The WAMP or application URI of the PubSub topic to subscribe to. 2724 :type topic: str 2725 2726 :param match: The topic matching method to be used for the subscription. 2727 :type match: str 2728 2729 :param get_retained: Whether the client wants the retained message we may have along with the subscription. 2730 :type get_retained: bool or None 2731 2732 :param forward_for: When this Subscribe is forwarded over a router-to-router link, 2733 or via an intermediary router. 2734 :type forward_for: list[dict] 2735 """ 2736 assert(type(request) in six.integer_types) 2737 assert(type(topic) == six.text_type) 2738 assert(match is None or type(match) == six.text_type) 2739 assert(match is None or match in [Subscribe.MATCH_EXACT, Subscribe.MATCH_PREFIX, Subscribe.MATCH_WILDCARD]) 2740 assert(get_retained is None or type(get_retained) is bool) 2741 assert(forward_for is None or type(forward_for) == list) 2742 if forward_for: 2743 for ff in forward_for: 2744 assert type(ff) == dict 2745 assert 'session' in ff and type(ff['session']) in six.integer_types 2746 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 2747 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 2748 2749 Message.__init__(self) 2750 self.request = request 2751 self.topic = topic 2752 self.match = match or Subscribe.MATCH_EXACT 2753 self.get_retained = get_retained 2754 self.forward_for = forward_for 2755 2756 @staticmethod 2757 def parse(wmsg): 2758 """ 2759 Verifies and parses an unserialized raw message into an actual WAMP message instance. 2760 2761 :param wmsg: The unserialized raw message. 2762 :type wmsg: list 2763 2764 :returns: An instance of this class. 2765 """ 2766 # this should already be verified by WampSerializer.unserialize 2767 assert(len(wmsg) > 0 and wmsg[0] == Subscribe.MESSAGE_TYPE) 2768 2769 if len(wmsg) != 4: 2770 raise ProtocolError("invalid message length {0} for SUBSCRIBE".format(len(wmsg))) 2771 2772 request = check_or_raise_id(wmsg[1], u"'request' in SUBSCRIBE") 2773 options = check_or_raise_extra(wmsg[2], u"'options' in SUBSCRIBE") 2774 topic = check_or_raise_uri(wmsg[3], u"'topic' in SUBSCRIBE", allow_empty_components=True) 2775 2776 match = Subscribe.MATCH_EXACT 2777 get_retained = None 2778 forward_for = None 2779 2780 if u'match' in options: 2781 2782 option_match = options[u'match'] 2783 if type(option_match) != six.text_type: 2784 raise ProtocolError("invalid type {0} for 'match' option in SUBSCRIBE".format(type(option_match))) 2785 2786 if option_match not in [Subscribe.MATCH_EXACT, Subscribe.MATCH_PREFIX, Subscribe.MATCH_WILDCARD]: 2787 raise ProtocolError("invalid value {0} for 'match' option in SUBSCRIBE".format(option_match)) 2788 2789 match = option_match 2790 2791 if u'get_retained' in options: 2792 get_retained = options[u'get_retained'] 2793 2794 if type(get_retained) != bool: 2795 raise ProtocolError("invalid type {0} for 'get_retained' option in SUBSCRIBE".format(type(get_retained))) 2796 2797 if u'forward_for' in options: 2798 forward_for = options[u'forward_for'] 2799 valid = False 2800 if type(forward_for) == list: 2801 for ff in forward_for: 2802 if type(ff) != dict: 2803 break 2804 if 'session' not in ff or type(ff['session']) not in six.integer_types: 2805 break 2806 if 'authid' not in ff or type(ff['authid']) != six.text_type: 2807 break 2808 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 2809 break 2810 valid = True 2811 2812 if not valid: 2813 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in SUBSCRIBE") 2814 2815 obj = Subscribe(request, topic, match=match, get_retained=get_retained, forward_for=forward_for) 2816 2817 return obj 2818 2819 def marshal_options(self): 2820 options = {} 2821 2822 if self.match and self.match != Subscribe.MATCH_EXACT: 2823 options[u'match'] = self.match 2824 2825 if self.get_retained is not None: 2826 options[u'get_retained'] = self.get_retained 2827 2828 if self.forward_for is not None: 2829 options[u'forward_for'] = self.forward_for 2830 2831 return options 2832 2833 def marshal(self): 2834 """ 2835 Marshal this object into a raw message for subsequent serialization to bytes. 2836 2837 :returns: The serialized raw message. 2838 :rtype: list 2839 """ 2840 return [Subscribe.MESSAGE_TYPE, self.request, self.marshal_options(), self.topic] 2841 2842 def __str__(self): 2843 """ 2844 Returns string representation of this message. 2845 """ 2846 return u"Subscribe(request={0}, topic={1}, match={2}, get_retained={3}, forward_for={4})".format(self.request, self.topic, self.match, self.get_retained, self.forward_for) 2847 2848 2849class Subscribed(Message): 2850 """ 2851 A WAMP ``SUBSCRIBED`` message. 2852 2853 Format: ``[SUBSCRIBED, SUBSCRIBE.Request|id, Subscription|id]`` 2854 """ 2855 2856 MESSAGE_TYPE = 33 2857 """ 2858 The WAMP message code for this type of message. 2859 """ 2860 2861 __slots__ = ( 2862 'request', 2863 'subscription', 2864 ) 2865 2866 def __init__(self, request, subscription): 2867 """ 2868 2869 :param request: The request ID of the original ``SUBSCRIBE`` request. 2870 :type request: int 2871 2872 :param subscription: The subscription ID for the subscribed topic (or topic pattern). 2873 :type subscription: int 2874 """ 2875 assert(type(request) in six.integer_types) 2876 assert(type(subscription) in six.integer_types) 2877 2878 Message.__init__(self) 2879 self.request = request 2880 self.subscription = subscription 2881 2882 @staticmethod 2883 def parse(wmsg): 2884 """ 2885 Verifies and parses an unserialized raw message into an actual WAMP message instance. 2886 2887 :param wmsg: The unserialized raw message. 2888 :type wmsg: list 2889 2890 :returns: An instance of this class. 2891 """ 2892 # this should already be verified by WampSerializer.unserialize 2893 assert(len(wmsg) > 0 and wmsg[0] == Subscribed.MESSAGE_TYPE) 2894 2895 if len(wmsg) != 3: 2896 raise ProtocolError("invalid message length {0} for SUBSCRIBED".format(len(wmsg))) 2897 2898 request = check_or_raise_id(wmsg[1], u"'request' in SUBSCRIBED") 2899 subscription = check_or_raise_id(wmsg[2], u"'subscription' in SUBSCRIBED") 2900 2901 obj = Subscribed(request, subscription) 2902 2903 return obj 2904 2905 def marshal(self): 2906 """ 2907 Marshal this object into a raw message for subsequent serialization to bytes. 2908 2909 :returns: The serialized raw message. 2910 :rtype: list 2911 """ 2912 return [Subscribed.MESSAGE_TYPE, self.request, self.subscription] 2913 2914 def __str__(self): 2915 """ 2916 Returns string representation of this message. 2917 """ 2918 return u"Subscribed(request={0}, subscription={1})".format(self.request, self.subscription) 2919 2920 2921class Unsubscribe(Message): 2922 """ 2923 A WAMP ``UNSUBSCRIBE`` message. 2924 2925 Formats: 2926 2927 * ``[UNSUBSCRIBE, Request|id, SUBSCRIBED.Subscription|id]`` 2928 * ``[UNSUBSCRIBE, Request|id, SUBSCRIBED.Subscription|id, Options|dict]`` 2929 """ 2930 2931 MESSAGE_TYPE = 34 2932 """ 2933 The WAMP message code for this type of message. 2934 """ 2935 2936 __slots__ = ( 2937 'request', 2938 'subscription', 2939 'forward_for', 2940 ) 2941 2942 def __init__(self, request, subscription, forward_for=None): 2943 """ 2944 2945 :param request: The WAMP request ID of this request. 2946 :type request: int 2947 2948 :param subscription: The subscription ID for the subscription to unsubscribe from. 2949 :type subscription: int 2950 2951 :param forward_for: When this Unsubscribe is forwarded over a router-to-router link, 2952 or via an intermediary router. 2953 :type forward_for: list[dict] 2954 """ 2955 assert(type(request) in six.integer_types) 2956 assert(type(subscription) in six.integer_types) 2957 if forward_for: 2958 for ff in forward_for: 2959 assert type(ff) == dict 2960 assert 'session' in ff and type(ff['session']) in six.integer_types 2961 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 2962 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 2963 2964 Message.__init__(self) 2965 self.request = request 2966 self.subscription = subscription 2967 self.forward_for = forward_for 2968 2969 @staticmethod 2970 def parse(wmsg): 2971 """ 2972 Verifies and parses an unserialized raw message into an actual WAMP message instance. 2973 2974 :param wmsg: The unserialized raw message. 2975 :type wmsg: list 2976 2977 :returns: An instance of this class. 2978 """ 2979 # this should already be verified by WampSerializer.unserialize 2980 assert(len(wmsg) > 0 and wmsg[0] == Unsubscribe.MESSAGE_TYPE) 2981 2982 if len(wmsg) not in [3, 4]: 2983 raise ProtocolError("invalid message length {0} for WAMP UNSUBSCRIBE".format(len(wmsg))) 2984 2985 request = check_or_raise_id(wmsg[1], u"'request' in UNSUBSCRIBE") 2986 subscription = check_or_raise_id(wmsg[2], u"'subscription' in UNSUBSCRIBE") 2987 2988 options = None 2989 if len(wmsg) > 3: 2990 options = check_or_raise_extra(wmsg[3], u"'options' in UNSUBSCRIBE") 2991 2992 forward_for = None 2993 if options and u'forward_for' in options: 2994 forward_for = options[u'forward_for'] 2995 valid = False 2996 if type(forward_for) == list: 2997 for ff in forward_for: 2998 if type(ff) != dict: 2999 break 3000 if 'session' not in ff or type(ff['session']) not in six.integer_types: 3001 break 3002 if 'authid' not in ff or type(ff['authid']) != six.text_type: 3003 break 3004 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 3005 break 3006 valid = True 3007 3008 if not valid: 3009 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in UNSUBSCRIBE") 3010 3011 obj = Unsubscribe(request, subscription, forward_for=forward_for) 3012 3013 return obj 3014 3015 def marshal(self): 3016 """ 3017 Marshal this object into a raw message for subsequent serialization to bytes. 3018 3019 :returns: The serialized raw message. 3020 :rtype: list 3021 """ 3022 if self.forward_for: 3023 options = { 3024 u'forward_for': self.forward_for, 3025 } 3026 return [Unsubscribe.MESSAGE_TYPE, self.request, self.subscription, options] 3027 else: 3028 return [Unsubscribe.MESSAGE_TYPE, self.request, self.subscription] 3029 3030 def __str__(self): 3031 """ 3032 Returns string representation of this message. 3033 """ 3034 return u"Unsubscribe(request={0}, subscription={1}, forward_for={2})".format(self.request, self.subscription, self.forward_for) 3035 3036 3037class Unsubscribed(Message): 3038 """ 3039 A WAMP ``UNSUBSCRIBED`` message. 3040 3041 Formats: 3042 3043 * ``[UNSUBSCRIBED, UNSUBSCRIBE.Request|id]`` 3044 * ``[UNSUBSCRIBED, UNSUBSCRIBE.Request|id, Details|dict]`` 3045 """ 3046 3047 MESSAGE_TYPE = 35 3048 """ 3049 The WAMP message code for this type of message. 3050 """ 3051 3052 __slots__ = ( 3053 'request', 3054 'subscription', 3055 'reason', 3056 ) 3057 3058 def __init__(self, request, subscription=None, reason=None): 3059 """ 3060 3061 :param request: The request ID of the original ``UNSUBSCRIBE`` request or 3062 ``0`` is router triggered unsubscribe ("router revocation signaling"). 3063 :type request: int 3064 3065 :param subscription: If unsubscribe was actively triggered by router, the ID 3066 of the subscription revoked. 3067 :type subscription: int or None 3068 3069 :param reason: The reason (an URI) for an active (router initiated) revocation. 3070 :type reason: str or None. 3071 """ 3072 assert(type(request) in six.integer_types) 3073 assert(subscription is None or type(subscription) in six.integer_types) 3074 assert(reason is None or type(reason) == six.text_type) 3075 assert((request != 0 and subscription is None) or (request == 0 and subscription != 0)) 3076 3077 Message.__init__(self) 3078 self.request = request 3079 self.subscription = subscription 3080 self.reason = reason 3081 3082 @staticmethod 3083 def parse(wmsg): 3084 """ 3085 Verifies and parses an unserialized raw message into an actual WAMP message instance. 3086 3087 :param wmsg: The unserialized raw message. 3088 :type wmsg: list 3089 3090 :returns: An instance of this class. 3091 """ 3092 # this should already be verified by WampSerializer.unserialize 3093 assert(len(wmsg) > 0 and wmsg[0] == Unsubscribed.MESSAGE_TYPE) 3094 3095 if len(wmsg) not in [2, 3]: 3096 raise ProtocolError("invalid message length {0} for UNSUBSCRIBED".format(len(wmsg))) 3097 3098 request = check_or_raise_id(wmsg[1], u"'request' in UNSUBSCRIBED") 3099 3100 subscription = None 3101 reason = None 3102 3103 if len(wmsg) > 2: 3104 3105 details = check_or_raise_extra(wmsg[2], u"'details' in UNSUBSCRIBED") 3106 3107 if u"subscription" in details: 3108 details_subscription = details[u"subscription"] 3109 if type(details_subscription) not in six.integer_types: 3110 raise ProtocolError("invalid type {0} for 'subscription' detail in UNSUBSCRIBED".format(type(details_subscription))) 3111 subscription = details_subscription 3112 3113 if u"reason" in details: 3114 reason = check_or_raise_uri(details[u"reason"], u"'reason' in UNSUBSCRIBED") 3115 3116 obj = Unsubscribed(request, subscription, reason) 3117 3118 return obj 3119 3120 def marshal(self): 3121 """ 3122 Marshal this object into a raw message for subsequent serialization to bytes. 3123 3124 :returns: The serialized raw message. 3125 :rtype: list 3126 """ 3127 if self.reason is not None or self.subscription is not None: 3128 details = {} 3129 if self.reason is not None: 3130 details[u"reason"] = self.reason 3131 if self.subscription is not None: 3132 details[u"subscription"] = self.subscription 3133 return [Unsubscribed.MESSAGE_TYPE, self.request, details] 3134 else: 3135 return [Unsubscribed.MESSAGE_TYPE, self.request] 3136 3137 def __str__(self): 3138 """ 3139 Returns string representation of this message. 3140 """ 3141 return u"Unsubscribed(request={0}, reason={1}, subscription={2})".format(self.request, self.reason, self.subscription) 3142 3143 3144class Event(Message): 3145 """ 3146 A WAMP ``EVENT`` message. 3147 3148 Formats: 3149 3150 * ``[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict]`` 3151 * ``[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Arguments|list]`` 3152 * ``[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Arguments|list, PUBLISH.ArgumentsKw|dict]`` 3153 * ``[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Payload|binary]`` 3154 """ 3155 3156 MESSAGE_TYPE = 36 3157 """ 3158 The WAMP message code for this type of message. 3159 """ 3160 3161 __slots__ = ( 3162 # uint64 3163 '_subscription', 3164 3165 # uint64 3166 '_publication', 3167 3168 # [uint8] 3169 '_args', 3170 3171 # [uint8] 3172 '_kwargs', 3173 3174 # [uint8] 3175 '_payload', 3176 3177 # Payload => uint8 3178 '_enc_algo', 3179 3180 # Serializer => uint8 3181 '_enc_serializer', 3182 3183 # [uint8] 3184 '_enc_key', 3185 3186 # uint64 3187 '_publisher', 3188 3189 # string (principal) 3190 '_publisher_authid', 3191 3192 # string (principal) 3193 '_publisher_authrole', 3194 3195 # string (uri) 3196 '_topic', 3197 3198 # bool 3199 '_retained', 3200 3201 # bool - FIXME: rename to "acknowledge" 3202 '_x_acknowledged_delivery', 3203 3204 # [Principal] 3205 '_forward_for', 3206 ) 3207 3208 def __init__(self, subscription=None, publication=None, args=None, kwargs=None, payload=None, 3209 publisher=None, publisher_authid=None, publisher_authrole=None, topic=None, 3210 retained=None, x_acknowledged_delivery=None, 3211 enc_algo=None, enc_key=None, enc_serializer=None, forward_for=None, 3212 from_fbs=None): 3213 """ 3214 3215 :param subscription: The subscription ID this event is dispatched under. 3216 :type subscription: int 3217 3218 :param publication: The publication ID of the dispatched event. 3219 :type publication: int 3220 3221 :param args: Positional values for application-defined exception. 3222 Must be serializable using any serializers in use. 3223 :type args: list or tuple or None 3224 3225 :param kwargs: Keyword values for application-defined exception. 3226 Must be serializable using any serializers in use. 3227 :type kwargs: dict or None 3228 3229 :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset. 3230 :type payload: bytes or None 3231 3232 :param publisher: The WAMP session ID of the publisher. Only filled if publisher is disclosed. 3233 :type publisher: None or int 3234 3235 :param publisher_authid: The WAMP authid of the publisher. Only filled if publisher is disclosed. 3236 :type publisher_authid: None or unicode 3237 3238 :param publisher_authrole: The WAMP authrole of the publisher. Only filled if publisher is disclosed. 3239 :type publisher_authrole: None or unicode 3240 3241 :param topic: For pattern-based subscriptions, the event MUST contain the actual topic published to. 3242 :type topic: str or None 3243 3244 :param retained: Whether the message was retained by the broker on the topic, rather than just published. 3245 :type retained: bool or None 3246 3247 :param x_acknowledged_delivery: Whether this Event should be acknowledged. 3248 :type x_acknowledged_delivery: bool or None 3249 3250 :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload. 3251 :type enc_algo: str or None 3252 3253 :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key. 3254 :type enc_key: str or None 3255 3256 :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload. 3257 :type enc_serializer: str or None 3258 3259 :param forward_for: When this Event is forwarded for a client (or from an intermediary router). 3260 :type forward_for: list[dict] 3261 """ 3262 assert(subscription is None or type(subscription) in six.integer_types) 3263 assert(publication is None or type(publication) in six.integer_types) 3264 assert(args is None or type(args) in [list, tuple]) 3265 assert(kwargs is None or type(kwargs) == dict) 3266 assert(payload is None or type(payload) == six.binary_type) 3267 assert(payload is None or (payload is not None and args is None and kwargs is None)) 3268 assert(publisher is None or type(publisher) in six.integer_types) 3269 assert(publisher_authid is None or type(publisher_authid) == six.text_type) 3270 assert(publisher_authrole is None or type(publisher_authrole) == six.text_type) 3271 assert(topic is None or type(topic) == six.text_type) 3272 assert(retained is None or type(retained) == bool) 3273 assert(x_acknowledged_delivery is None or type(x_acknowledged_delivery) == bool) 3274 assert(enc_algo is None or is_valid_enc_algo(enc_algo)) 3275 assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None)) 3276 assert(enc_key is None or type(enc_key) == six.text_type) 3277 assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer)) 3278 3279 assert(forward_for is None or type(forward_for) == list) 3280 if forward_for: 3281 for ff in forward_for: 3282 assert type(ff) == dict 3283 assert 'session' in ff and type(ff['session']) in six.integer_types 3284 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 3285 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 3286 3287 Message.__init__(self, from_fbs=from_fbs) 3288 self._subscription = subscription 3289 self._publication = publication 3290 self._args = args 3291 self._kwargs = _validate_kwargs(kwargs) 3292 self._payload = payload 3293 self._publisher = publisher 3294 self._publisher_authid = publisher_authid 3295 self._publisher_authrole = publisher_authrole 3296 self._topic = topic 3297 self._retained = retained 3298 self._x_acknowledged_delivery = x_acknowledged_delivery 3299 self._enc_algo = enc_algo 3300 self._enc_key = enc_key 3301 self._enc_serializer = enc_serializer 3302 self._forward_for = forward_for 3303 3304 def __eq__(self, other): 3305 if not isinstance(other, self.__class__): 3306 return False 3307 if not Message.__eq__(self, other): 3308 return False 3309 if other.subscription != self.subscription: 3310 return False 3311 if other.publication != self.publication: 3312 return False 3313 if other.args != self.args: 3314 return False 3315 if other.kwargs != self.kwargs: 3316 return False 3317 if other.payload != self.payload: 3318 return False 3319 if other.publisher != self.publisher: 3320 return False 3321 if other.publisher_authid != self.publisher_authid: 3322 return False 3323 if other.publisher_authrole != self.publisher_authrole: 3324 return False 3325 if other.topic != self.topic: 3326 return False 3327 if other.retained != self.retained: 3328 return False 3329 if other.x_acknowledged_delivery != self.x_acknowledged_delivery: 3330 return False 3331 if other.enc_algo != self.enc_algo: 3332 return False 3333 if other.enc_key != self.enc_key: 3334 return False 3335 if other.enc_serializer != self.enc_serializer: 3336 return False 3337 if other.forward_for != self.forward_for: 3338 return False 3339 return True 3340 3341 def __ne__(self, other): 3342 return not self.__eq__(other) 3343 3344 @property 3345 def subscription(self): 3346 if self._subscription is None and self._from_fbs: 3347 self._subscription = self._from_fbs.Subscription() 3348 return self._subscription 3349 3350 @subscription.setter 3351 def subscription(self, value): 3352 assert(value is None or type(value) in six.integer_types) 3353 self._subscription = value 3354 3355 @property 3356 def publication(self): 3357 if self._publication is None and self._from_fbs: 3358 self._publication = self._from_fbs.Publication() 3359 return self._publication 3360 3361 @publication.setter 3362 def publication(self, value): 3363 assert(value is None or type(value) in six.integer_types) 3364 self._publication = value 3365 3366 @property 3367 def args(self): 3368 if self._args is None and self._from_fbs: 3369 if self._from_fbs.ArgsLength(): 3370 self._args = cbor.loads(bytes(self._from_fbs.ArgsAsBytes())) 3371 return self._args 3372 3373 @args.setter 3374 def args(self, value): 3375 assert(value is None or type(value) in [list, tuple]) 3376 self._args = value 3377 3378 @property 3379 def kwargs(self): 3380 if self._kwargs is None and self._from_fbs: 3381 if self._from_fbs.KwargsLength(): 3382 self._kwargs = cbor.loads(bytes(self._from_fbs.KwargsAsBytes())) 3383 return self._kwargs 3384 3385 @kwargs.setter 3386 def kwargs(self, value): 3387 assert(value is None or type(value) == dict) 3388 self._kwargs = value 3389 3390 @property 3391 def payload(self): 3392 if self._payload is None and self._from_fbs: 3393 if self._from_fbs.PayloadLength(): 3394 self._payload = self._from_fbs.PayloadAsBytes() 3395 return self._payload 3396 3397 @payload.setter 3398 def payload(self, value): 3399 assert value is None or type(value) == bytes 3400 self._payload = value 3401 3402 @property 3403 def publisher(self): 3404 if self._publisher is None and self._from_fbs: 3405 publisher = self._from_fbs.Publisher() 3406 if publisher: 3407 self._publisher = publisher 3408 return self._publisher 3409 3410 @publisher.setter 3411 def publisher(self, value): 3412 assert value is None or type(value) == int 3413 self._publisher = value 3414 3415 @property 3416 def publisher_authid(self): 3417 if self._publisher_authid is None and self._from_fbs: 3418 s = self._from_fbs.PublisherAuthid() 3419 if s: 3420 self._publisher_authid = s.decode('utf8') 3421 return self._publisher_authid 3422 3423 @publisher_authid.setter 3424 def publisher_authid(self, value): 3425 assert value is None or type(value) == str 3426 self._publisher_authid = value 3427 3428 @property 3429 def publisher_authrole(self): 3430 if self._publisher_authrole is None and self._from_fbs: 3431 s = self._from_fbs.PublisherAuthrole() 3432 if s: 3433 self._publisher_authrole = s.decode('utf8') 3434 return self._publisher_authrole 3435 3436 @publisher_authrole.setter 3437 def publisher_authrole(self, value): 3438 assert value is None or type(value) == str 3439 self._publisher_authrole = value 3440 3441 @property 3442 def topic(self): 3443 if self._topic is None and self._from_fbs: 3444 s = self._from_fbs.Topic() 3445 if s: 3446 self._topic = s.decode('utf8') 3447 return self._topic 3448 3449 @topic.setter 3450 def topic(self, value): 3451 assert value is None or type(value) == str 3452 self._topic = value 3453 3454 @property 3455 def retained(self): 3456 if self._retained is None and self._from_fbs: 3457 self._retained = self._from_fbs.Retained() 3458 return self._retained 3459 3460 @retained.setter 3461 def retained(self, value): 3462 assert value is None or type(value) == bool 3463 self._retained = value 3464 3465 @property 3466 def x_acknowledged_delivery(self): 3467 if self._x_acknowledged_delivery is None and self._from_fbs: 3468 x_acknowledged_delivery = self._from_fbs.Acknowledge() 3469 if x_acknowledged_delivery: 3470 self._x_acknowledged_delivery = x_acknowledged_delivery 3471 return self._x_acknowledged_delivery 3472 3473 @x_acknowledged_delivery.setter 3474 def x_acknowledged_delivery(self, value): 3475 assert value is None or type(value) == bool 3476 self._x_acknowledged_delivery = value 3477 3478 @property 3479 def enc_algo(self): 3480 if self._enc_algo is None and self._from_fbs: 3481 enc_algo = self._from_fbs.EncAlgo() 3482 if enc_algo: 3483 self._enc_algo = enc_algo 3484 return self._enc_algo 3485 3486 @enc_algo.setter 3487 def enc_algo(self, value): 3488 assert value is None or value in [ENC_ALGO_CRYPTOBOX, ENC_ALGO_MQTT, ENC_ALGO_XBR] 3489 self._enc_algo = value 3490 3491 @property 3492 def enc_key(self): 3493 if self._enc_key is None and self._from_fbs: 3494 if self._from_fbs.EncKeyLength(): 3495 self._enc_key = self._from_fbs.EncKeyAsBytes() 3496 return self._enc_key 3497 3498 @enc_key.setter 3499 def enc_key(self, value): 3500 assert value is None or type(value) == bytes 3501 self._enc_key = value 3502 3503 @property 3504 def enc_serializer(self): 3505 if self._enc_serializer is None and self._from_fbs: 3506 enc_serializer = self._from_fbs.EncSerializer() 3507 if enc_serializer: 3508 self._enc_serializer = enc_serializer 3509 return self._enc_serializer 3510 3511 @enc_serializer.setter 3512 def enc_serializer(self, value): 3513 assert value is None or value in [ENC_SER_JSON, ENC_SER_MSGPACK, ENC_SER_CBOR, ENC_SER_UBJSON] 3514 self._enc_serializer = value 3515 3516 @property 3517 def forward_for(self): 3518 # FIXME 3519 return self._forward_for 3520 3521 @forward_for.setter 3522 def forward_for(self, value): 3523 # FIXME 3524 self._forward_for = value 3525 3526 @staticmethod 3527 def cast(buf): 3528 return Event(from_fbs=message_fbs.Event.GetRootAsEvent(buf, 0)) 3529 3530 def build(self, builder): 3531 3532 args = self.args 3533 if args: 3534 args = builder.CreateByteVector(cbor.dumps(args)) 3535 3536 kwargs = self.kwargs 3537 if kwargs: 3538 kwargs = builder.CreateByteVector(cbor.dumps(kwargs)) 3539 3540 payload = self.payload 3541 if payload: 3542 payload = builder.CreateByteVector(payload) 3543 3544 publisher_authid = self.publisher_authid 3545 if publisher_authid: 3546 publisher_authid = builder.CreateString(publisher_authid) 3547 3548 publisher_authrole = self.publisher_authrole 3549 if publisher_authrole: 3550 publisher_authrole = builder.CreateString(publisher_authrole) 3551 3552 topic = self.topic 3553 if topic: 3554 topic = builder.CreateString(topic) 3555 3556 enc_key = self.enc_key 3557 if enc_key: 3558 enc_key = builder.CreateByteVector(enc_key) 3559 3560 message_fbs.EventGen.EventStart(builder) 3561 3562 if self.subscription: 3563 message_fbs.EventGen.EventAddSubscription(builder, self.subscription) 3564 if self.publication: 3565 message_fbs.EventGen.EventAddPublication(builder, self.publication) 3566 3567 if args: 3568 message_fbs.EventGen.EventAddArgs(builder, args) 3569 if kwargs: 3570 message_fbs.EventGen.EventAddKwargs(builder, kwargs) 3571 if payload: 3572 message_fbs.EventGen.EventAddPayload(builder, payload) 3573 3574 if self.publisher: 3575 message_fbs.EventGen.EventAddPublisher(builder, self.publisher) 3576 if publisher_authid: 3577 message_fbs.EventGen.EventAddPublisherAuthid(builder, publisher_authid) 3578 if publisher_authrole: 3579 message_fbs.EventGen.EventAddPublisherAuthrole(builder, publisher_authrole) 3580 3581 if topic: 3582 message_fbs.EventGen.EventAddTopic(builder, topic) 3583 if self.retained is not None: 3584 message_fbs.EventGen.EventAddRetained(builder, self.retained) 3585 if self.x_acknowledged_delivery is not None: 3586 message_fbs.EventGen.EventAddAcknowledge(builder, self.x_acknowledged_delivery) 3587 3588 if self.enc_algo: 3589 message_fbs.EventGen.EventAddEncAlgo(builder, self.enc_algo) 3590 if enc_key: 3591 message_fbs.EventGen.EventAddEncKey(builder, enc_key) 3592 if self.enc_serializer: 3593 message_fbs.EventGen.EventAddEncSerializer(builder, self.enc_serializer) 3594 3595 # FIXME: add forward_for 3596 3597 msg = message_fbs.EventGen.EventEnd(builder) 3598 3599 message_fbs.Message.MessageStart(builder) 3600 message_fbs.Message.MessageAddMsgType(builder, message_fbs.MessageType.EVENT) 3601 message_fbs.Message.MessageAddMsg(builder, msg) 3602 union_msg = message_fbs.Message.MessageEnd(builder) 3603 3604 return union_msg 3605 3606 @staticmethod 3607 def parse(wmsg): 3608 """ 3609 Verifies and parses an unserialized raw message into an actual WAMP message instance. 3610 3611 :param wmsg: The unserialized raw message. 3612 :type wmsg: list 3613 3614 :returns: An instance of this class. 3615 """ 3616 # this should already be verified by WampSerializer.unserialize 3617 assert(len(wmsg) > 0 and wmsg[0] == Event.MESSAGE_TYPE) 3618 3619 if len(wmsg) not in (4, 5, 6): 3620 raise ProtocolError("invalid message length {0} for EVENT".format(len(wmsg))) 3621 3622 subscription = check_or_raise_id(wmsg[1], u"'subscription' in EVENT") 3623 publication = check_or_raise_id(wmsg[2], u"'publication' in EVENT") 3624 details = check_or_raise_extra(wmsg[3], u"'details' in EVENT") 3625 3626 args = None 3627 kwargs = None 3628 payload = None 3629 enc_algo = None 3630 enc_key = None 3631 enc_serializer = None 3632 3633 if len(wmsg) == 5 and type(wmsg[4]) == six.binary_type: 3634 3635 payload = wmsg[4] 3636 3637 enc_algo = details.get(u'enc_algo', None) 3638 if enc_algo and not is_valid_enc_algo(enc_algo): 3639 raise ProtocolError("invalid value {0} for 'enc_algo' detail in EVENT".format(enc_algo)) 3640 3641 enc_key = details.get(u'enc_key', None) 3642 if enc_key and type(enc_key) != six.text_type: 3643 raise ProtocolError("invalid type {0} for 'enc_key' detail in EVENT".format(type(enc_key))) 3644 3645 enc_serializer = details.get(u'enc_serializer', None) 3646 if enc_serializer and not is_valid_enc_serializer(enc_serializer): 3647 raise ProtocolError("invalid value {0} for 'enc_serializer' detail in EVENT".format(enc_serializer)) 3648 3649 else: 3650 if len(wmsg) > 4: 3651 args = wmsg[4] 3652 if args is not None and type(args) != list: 3653 raise ProtocolError("invalid type {0} for 'args' in EVENT".format(type(args))) 3654 if len(wmsg) > 5: 3655 kwargs = wmsg[5] 3656 if type(kwargs) != dict: 3657 raise ProtocolError("invalid type {0} for 'kwargs' in EVENT".format(type(kwargs))) 3658 3659 publisher = None 3660 publisher_authid = None 3661 publisher_authrole = None 3662 topic = None 3663 retained = None 3664 forward_for = None 3665 x_acknowledged_delivery = None 3666 3667 if u'publisher' in details: 3668 3669 detail_publisher = details[u'publisher'] 3670 if type(detail_publisher) not in six.integer_types: 3671 raise ProtocolError("invalid type {0} for 'publisher' detail in EVENT".format(type(detail_publisher))) 3672 3673 publisher = detail_publisher 3674 3675 if u'publisher_authid' in details: 3676 3677 detail_publisher_authid = details[u'publisher_authid'] 3678 if type(detail_publisher_authid) != six.text_type: 3679 raise ProtocolError("invalid type {0} for 'publisher_authid' detail in EVENT".format(type(detail_publisher_authid))) 3680 3681 publisher_authid = detail_publisher_authid 3682 3683 if u'publisher_authrole' in details: 3684 3685 detail_publisher_authrole = details[u'publisher_authrole'] 3686 if type(detail_publisher_authrole) != six.text_type: 3687 raise ProtocolError("invalid type {0} for 'publisher_authrole' detail in EVENT".format(type(detail_publisher_authrole))) 3688 3689 publisher_authrole = detail_publisher_authrole 3690 3691 if u'topic' in details: 3692 3693 detail_topic = details[u'topic'] 3694 if type(detail_topic) != six.text_type: 3695 raise ProtocolError("invalid type {0} for 'topic' detail in EVENT".format(type(detail_topic))) 3696 3697 topic = detail_topic 3698 3699 if u'retained' in details: 3700 retained = details[u'retained'] 3701 if type(retained) != bool: 3702 raise ProtocolError("invalid type {0} for 'retained' detail in EVENT".format(type(retained))) 3703 3704 if u'x_acknowledged_delivery' in details: 3705 x_acknowledged_delivery = details[u'x_acknowledged_delivery'] 3706 if type(x_acknowledged_delivery) != bool: 3707 raise ProtocolError("invalid type {0} for 'x_acknowledged_delivery' detail in EVENT".format(type(x_acknowledged_delivery))) 3708 3709 if u'forward_for' in details: 3710 forward_for = details[u'forward_for'] 3711 valid = False 3712 if type(forward_for) == list: 3713 for ff in forward_for: 3714 if type(ff) != dict: 3715 break 3716 if 'session' not in ff or type(ff['session']) not in six.integer_types: 3717 break 3718 if 'authid' not in ff or type(ff['authid']) != six.text_type: 3719 break 3720 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 3721 break 3722 valid = True 3723 3724 if not valid: 3725 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in EVENT") 3726 3727 obj = Event(subscription, 3728 publication, 3729 args=args, 3730 kwargs=kwargs, 3731 payload=payload, 3732 publisher=publisher, 3733 publisher_authid=publisher_authid, 3734 publisher_authrole=publisher_authrole, 3735 topic=topic, 3736 retained=retained, 3737 x_acknowledged_delivery=x_acknowledged_delivery, 3738 enc_algo=enc_algo, 3739 enc_key=enc_key, 3740 enc_serializer=enc_serializer, 3741 forward_for=forward_for) 3742 3743 return obj 3744 3745 def marshal(self): 3746 """ 3747 Marshal this object into a raw message for subsequent serialization to bytes. 3748 3749 :returns: The serialized raw message. 3750 :rtype: list 3751 """ 3752 details = {} 3753 3754 if self.publisher is not None: 3755 details[u'publisher'] = self.publisher 3756 3757 if self.publisher_authid is not None: 3758 details[u'publisher_authid'] = self.publisher_authid 3759 3760 if self.publisher_authrole is not None: 3761 details[u'publisher_authrole'] = self.publisher_authrole 3762 3763 if self.topic is not None: 3764 details[u'topic'] = self.topic 3765 3766 if self.retained is not None: 3767 details[u'retained'] = self.retained 3768 3769 if self.x_acknowledged_delivery is not None: 3770 details[u'x_acknowledged_delivery'] = self.x_acknowledged_delivery 3771 3772 if self.forward_for is not None: 3773 details[u'forward_for'] = self.forward_for 3774 3775 if self.payload: 3776 if self.enc_algo is not None: 3777 details[u'enc_algo'] = self.enc_algo 3778 if self.enc_key is not None: 3779 details[u'enc_key'] = self.enc_key 3780 if self.enc_serializer is not None: 3781 details[u'enc_serializer'] = self.enc_serializer 3782 return [Event.MESSAGE_TYPE, self.subscription, self.publication, details, self.payload] 3783 else: 3784 if self.kwargs: 3785 return [Event.MESSAGE_TYPE, self.subscription, self.publication, details, self.args, self.kwargs] 3786 elif self.args: 3787 return [Event.MESSAGE_TYPE, self.subscription, self.publication, details, self.args] 3788 else: 3789 return [Event.MESSAGE_TYPE, self.subscription, self.publication, details] 3790 3791 def __str__(self): 3792 """ 3793 Returns string representation of this message. 3794 """ 3795 return u"Event(subscription={}, publication={}, args={}, kwargs={}, publisher={}, publisher_authid={}, publisher_authrole={}, topic={}, retained={}, enc_algo={}, enc_key={}, enc_serializer={}, payload={}, forward_for={})".format(self.subscription, self.publication, self.args, self.kwargs, self.publisher, self.publisher_authid, self.publisher_authrole, self.topic, self.retained, self.enc_algo, self.enc_key, self.enc_serializer, b2a(self.payload), self.forward_for) 3796 3797 3798class EventReceived(Message): 3799 """ 3800 A WAMP ``EVENT_RECEIVED`` message. 3801 3802 Format: ``[EVENT_RECEIVED, EVENT.Publication|id]`` 3803 """ 3804 3805 # NOTE: Implementation-specific message! Should be 37 on ratification. 3806 MESSAGE_TYPE = 337 3807 """ 3808 The WAMP message code for this type of message. 3809 """ 3810 3811 __slots__ = ( 3812 'publication', 3813 ) 3814 3815 def __init__(self, publication): 3816 """ 3817 3818 :param publication: The publication ID for the sent event. 3819 :type publication: int 3820 """ 3821 assert(type(publication) in six.integer_types) 3822 3823 Message.__init__(self) 3824 self.publication = publication 3825 3826 @staticmethod 3827 def parse(wmsg): 3828 """ 3829 Verifies and parses an unserialized raw message into an actual WAMP message instance. 3830 3831 :param wmsg: The unserialized raw message. 3832 :type wmsg: list 3833 3834 :returns: An instance of this class. 3835 """ 3836 # this should already be verified by WampSerializer.unserialize 3837 assert(len(wmsg) > 0 and wmsg[0] == EventReceived.MESSAGE_TYPE) 3838 3839 if len(wmsg) != 2: 3840 raise ProtocolError("invalid message length {0} for EVENT_RECEIVED".format(len(wmsg))) 3841 3842 publication = check_or_raise_id(wmsg[1], u"'publication' in EVENT_RECEIVED") 3843 3844 obj = EventReceived(publication) 3845 3846 return obj 3847 3848 def marshal(self): 3849 """ 3850 Marshal this object into a raw message for subsequent serialization to bytes. 3851 3852 :returns: The serialized raw message. 3853 :rtype: list 3854 """ 3855 return [EventReceived.MESSAGE_TYPE, self.publication] 3856 3857 def __str__(self): 3858 """ 3859 Returns string representation of this message. 3860 """ 3861 return u"EventReceived(publication={})".format(self.publication) 3862 3863 3864class Call(Message): 3865 """ 3866 A WAMP ``CALL`` message. 3867 3868 Formats: 3869 3870 * ``[CALL, Request|id, Options|dict, Procedure|uri]`` 3871 * ``[CALL, Request|id, Options|dict, Procedure|uri, Arguments|list]`` 3872 * ``[CALL, Request|id, Options|dict, Procedure|uri, Arguments|list, ArgumentsKw|dict]`` 3873 * ``[CALL, Request|id, Options|dict, Procedure|uri, Payload|binary]`` 3874 """ 3875 3876 MESSAGE_TYPE = 48 3877 """ 3878 The WAMP message code for this type of message. 3879 """ 3880 3881 __slots__ = ( 3882 'request', 3883 'procedure', 3884 'args', 3885 'kwargs', 3886 'payload', 3887 'timeout', 3888 'receive_progress', 3889 'enc_algo', 3890 'enc_key', 3891 'enc_serializer', 3892 'caller', 3893 'caller_authid', 3894 'caller_authrole', 3895 'forward_for', 3896 ) 3897 3898 def __init__(self, 3899 request, 3900 procedure, 3901 args=None, 3902 kwargs=None, 3903 payload=None, 3904 timeout=None, 3905 receive_progress=None, 3906 enc_algo=None, 3907 enc_key=None, 3908 enc_serializer=None, 3909 caller=None, 3910 caller_authid=None, 3911 caller_authrole=None, 3912 forward_for=None): 3913 """ 3914 3915 :param request: The WAMP request ID of this request. 3916 :type request: int 3917 3918 :param procedure: The WAMP or application URI of the procedure which should be called. 3919 :type procedure: str 3920 3921 :param args: Positional values for application-defined call arguments. 3922 Must be serializable using any serializers in use. 3923 :type args: list or tuple or None 3924 3925 :param kwargs: Keyword values for application-defined call arguments. 3926 Must be serializable using any serializers in use. 3927 :type kwargs: dict or None 3928 3929 :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset. 3930 :type payload: bytes or None 3931 3932 :param timeout: If present, let the callee automatically cancel 3933 the call after this ms. 3934 :type timeout: int or None 3935 3936 :param receive_progress: If ``True``, indicates that the caller wants to receive 3937 progressive call results. 3938 :type receive_progress: bool or None 3939 3940 :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload. 3941 :type enc_algo: str or None 3942 3943 :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key. 3944 :type enc_key: str or None 3945 3946 :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload. 3947 :type enc_serializer: str or None 3948 3949 :param caller: The WAMP session ID of the caller. Only filled if caller is disclosed. 3950 :type caller: None or int 3951 3952 :param caller_authid: The WAMP authid of the caller. Only filled if caller is disclosed. 3953 :type caller_authid: None or unicode 3954 3955 :param caller_authrole: The WAMP authrole of the caller. Only filled if caller is disclosed. 3956 :type caller_authrole: None or unicode 3957 3958 :param forward_for: When this Publish is forwarded for a client (or from an intermediary router). 3959 :type forward_for: list[dict] 3960 """ 3961 assert(type(request) in six.integer_types) 3962 assert(type(procedure) == six.text_type) 3963 assert(args is None or type(args) in [list, tuple]) 3964 assert(kwargs is None or type(kwargs) == dict) 3965 assert(payload is None or type(payload) == six.binary_type) 3966 assert(payload is None or (payload is not None and args is None and kwargs is None)) 3967 assert(timeout is None or type(timeout) in six.integer_types) 3968 assert(receive_progress is None or type(receive_progress) == bool) 3969 3970 # payload transparency related knobs 3971 assert(enc_algo is None or is_valid_enc_algo(enc_algo)) 3972 assert(enc_key is None or type(enc_key) == six.text_type) 3973 assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer)) 3974 assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None)) 3975 3976 assert(caller is None or type(caller) in six.integer_types) 3977 assert(caller_authid is None or type(caller_authid) == six.text_type) 3978 assert(caller_authrole is None or type(caller_authrole) == six.text_type) 3979 3980 assert(forward_for is None or type(forward_for) == list) 3981 if forward_for: 3982 for ff in forward_for: 3983 assert type(ff) == dict 3984 assert 'session' in ff and type(ff['session']) in six.integer_types 3985 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 3986 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 3987 3988 Message.__init__(self) 3989 self.request = request 3990 self.procedure = procedure 3991 self.args = args 3992 self.kwargs = _validate_kwargs(kwargs) 3993 self.payload = payload 3994 self.timeout = timeout 3995 self.receive_progress = receive_progress 3996 3997 # payload transparency related knobs 3998 self.enc_algo = enc_algo 3999 self.enc_key = enc_key 4000 self.enc_serializer = enc_serializer 4001 4002 # message forwarding 4003 self.caller = caller 4004 self.caller_authid = caller_authid 4005 self.caller_authrole = caller_authrole 4006 self.forward_for = forward_for 4007 4008 @staticmethod 4009 def parse(wmsg): 4010 """ 4011 Verifies and parses an unserialized raw message into an actual WAMP message instance. 4012 4013 :param wmsg: The unserialized raw message. 4014 :type wmsg: list 4015 4016 :returns: An instance of this class. 4017 """ 4018 # this should already be verified by WampSerializer.unserialize 4019 assert(len(wmsg) > 0 and wmsg[0] == Call.MESSAGE_TYPE) 4020 4021 if len(wmsg) not in (4, 5, 6): 4022 raise ProtocolError("invalid message length {0} for CALL".format(len(wmsg))) 4023 4024 request = check_or_raise_id(wmsg[1], u"'request' in CALL") 4025 options = check_or_raise_extra(wmsg[2], u"'options' in CALL") 4026 procedure = check_or_raise_uri(wmsg[3], u"'procedure' in CALL") 4027 4028 args = None 4029 kwargs = None 4030 payload = None 4031 enc_algo = None 4032 enc_key = None 4033 enc_serializer = None 4034 4035 if len(wmsg) == 5 and type(wmsg[4]) in [six.text_type, six.binary_type]: 4036 4037 payload = wmsg[4] 4038 4039 enc_algo = options.get(u'enc_algo', None) 4040 if enc_algo and not is_valid_enc_algo(enc_algo): 4041 raise ProtocolError("invalid value {0} for 'enc_algo' detail in CALL".format(enc_algo)) 4042 4043 enc_key = options.get(u'enc_key', None) 4044 if enc_key and type(enc_key) != six.text_type: 4045 raise ProtocolError("invalid type {0} for 'enc_key' detail in CALL".format(type(enc_key))) 4046 4047 enc_serializer = options.get(u'enc_serializer', None) 4048 if enc_serializer and not is_valid_enc_serializer(enc_serializer): 4049 raise ProtocolError("invalid value {0} for 'enc_serializer' detail in CALL".format(enc_serializer)) 4050 4051 else: 4052 if len(wmsg) > 4: 4053 args = wmsg[4] 4054 if args is not None and type(args) != list: 4055 raise ProtocolError("invalid type {0} for 'args' in CALL".format(type(args))) 4056 4057 if len(wmsg) > 5: 4058 kwargs = wmsg[5] 4059 if type(kwargs) != dict: 4060 raise ProtocolError("invalid type {0} for 'kwargs' in CALL".format(type(kwargs))) 4061 4062 timeout = None 4063 receive_progress = None 4064 caller = None 4065 caller_authid = None 4066 caller_authrole = None 4067 forward_for = None 4068 4069 if u'timeout' in options: 4070 4071 option_timeout = options[u'timeout'] 4072 if type(option_timeout) not in six.integer_types: 4073 raise ProtocolError("invalid type {0} for 'timeout' option in CALL".format(type(option_timeout))) 4074 4075 if option_timeout < 0: 4076 raise ProtocolError("invalid value {0} for 'timeout' option in CALL".format(option_timeout)) 4077 4078 timeout = option_timeout 4079 4080 if u'receive_progress' in options: 4081 4082 option_receive_progress = options[u'receive_progress'] 4083 if type(option_receive_progress) != bool: 4084 raise ProtocolError("invalid type {0} for 'receive_progress' option in CALL".format(type(option_receive_progress))) 4085 4086 receive_progress = option_receive_progress 4087 4088 if u'caller' in options: 4089 4090 option_caller = options[u'caller'] 4091 if type(option_caller) not in six.integer_types: 4092 raise ProtocolError("invalid type {0} for 'caller' detail in CALL".format(type(option_caller))) 4093 4094 caller = option_caller 4095 4096 if u'caller_authid' in options: 4097 4098 option_caller_authid = options[u'caller_authid'] 4099 if type(option_caller_authid) != six.text_type: 4100 raise ProtocolError("invalid type {0} for 'caller_authid' detail in CALL".format(type(option_caller_authid))) 4101 4102 caller_authid = option_caller_authid 4103 4104 if u'caller_authrole' in options: 4105 4106 option_caller_authrole = options[u'caller_authrole'] 4107 if type(option_caller_authrole) != six.text_type: 4108 raise ProtocolError("invalid type {0} for 'caller_authrole' detail in CALL".format(type(option_caller_authrole))) 4109 4110 caller_authrole = option_caller_authrole 4111 4112 if u'forward_for' in options: 4113 forward_for = options[u'forward_for'] 4114 valid = False 4115 if type(forward_for) == list: 4116 for ff in forward_for: 4117 if type(ff) != dict: 4118 break 4119 if 'session' not in ff or type(ff['session']) not in six.integer_types: 4120 break 4121 if 'authid' not in ff or type(ff['authid']) != six.text_type: 4122 break 4123 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 4124 break 4125 valid = True 4126 4127 if not valid: 4128 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in CALL") 4129 4130 obj = Call(request, 4131 procedure, 4132 args=args, 4133 kwargs=kwargs, 4134 payload=payload, 4135 timeout=timeout, 4136 receive_progress=receive_progress, 4137 enc_algo=enc_algo, 4138 enc_key=enc_key, 4139 enc_serializer=enc_serializer, 4140 caller=caller, 4141 caller_authid=caller_authid, 4142 caller_authrole=caller_authrole, 4143 forward_for=forward_for) 4144 4145 return obj 4146 4147 def marshal_options(self): 4148 options = {} 4149 4150 if self.timeout is not None: 4151 options[u'timeout'] = self.timeout 4152 4153 if self.receive_progress is not None: 4154 options[u'receive_progress'] = self.receive_progress 4155 4156 if self.payload: 4157 if self.enc_algo is not None: 4158 options[u'enc_algo'] = self.enc_algo 4159 if self.enc_key is not None: 4160 options[u'enc_key'] = self.enc_key 4161 if self.enc_serializer is not None: 4162 options[u'enc_serializer'] = self.enc_serializer 4163 4164 if self.caller is not None: 4165 options[u'caller'] = self.caller 4166 if self.caller_authid is not None: 4167 options[u'caller_authid'] = self.caller_authid 4168 if self.caller_authrole is not None: 4169 options[u'caller_authrole'] = self.caller_authrole 4170 4171 if self.forward_for is not None: 4172 options[u'forward_for'] = self.forward_for 4173 4174 return options 4175 4176 def marshal(self): 4177 """ 4178 Marshal this object into a raw message for subsequent serialization to bytes. 4179 4180 :returns: The serialized raw message. 4181 :rtype: list 4182 """ 4183 options = self.marshal_options() 4184 4185 if self.payload: 4186 return [Call.MESSAGE_TYPE, self.request, options, self.procedure, self.payload] 4187 else: 4188 if self.kwargs: 4189 return [Call.MESSAGE_TYPE, self.request, options, self.procedure, self.args, self.kwargs] 4190 elif self.args: 4191 return [Call.MESSAGE_TYPE, self.request, options, self.procedure, self.args] 4192 else: 4193 return [Call.MESSAGE_TYPE, self.request, options, self.procedure] 4194 4195 def __str__(self): 4196 """ 4197 Returns string representation of this message. 4198 """ 4199 return u"Call(request={}, procedure={}, args={}, kwargs={}, timeout={}, receive_progress={}, enc_algo={}, enc_key={}, enc_serializer={}, payload={}, caller={}, caller_authid={}, caller_authrole={}, forward_for={})".format(self.request, self.procedure, self.args, self.kwargs, self.timeout, self.receive_progress, self.enc_algo, self.enc_key, self.enc_serializer, b2a(self.payload), self.caller, self.caller_authid, self.caller_authrole, self.forward_for) 4200 4201 4202class Cancel(Message): 4203 """ 4204 A WAMP ``CANCEL`` message. 4205 4206 Format: ``[CANCEL, CALL.Request|id, Options|dict]`` 4207 4208 See: https://wamp-proto.org/static/rfc/draft-oberstet-hybi-crossbar-wamp.html#rfc.section.14.3.4 4209 """ 4210 4211 MESSAGE_TYPE = 49 4212 """ 4213 The WAMP message code for this type of message. 4214 """ 4215 4216 SKIP = u'skip' 4217 KILL = u'kill' 4218 KILLNOWAIT = u'killnowait' 4219 4220 __slots__ = ( 4221 'request', 4222 'mode', 4223 'forward_for', 4224 ) 4225 4226 def __init__(self, request, mode=None, forward_for=None): 4227 """ 4228 4229 :param request: The WAMP request ID of the original `CALL` to cancel. 4230 :type request: int 4231 4232 :param mode: Specifies how to cancel the call (``"skip"``, ``"killnowait"`` or ``"kill"``). 4233 :type mode: str or None 4234 4235 :param forward_for: When this Cancel is forwarded for a client (or from an intermediary router). 4236 :type forward_for: list[dict] 4237 """ 4238 assert(type(request) in six.integer_types) 4239 assert(mode is None or type(mode) == six.text_type) 4240 assert(mode in [None, self.SKIP, self.KILLNOWAIT, self.KILL]) 4241 assert(forward_for is None or type(forward_for) == list) 4242 4243 if forward_for: 4244 for ff in forward_for: 4245 assert type(ff) == dict 4246 assert 'session' in ff and type(ff['session']) in six.integer_types 4247 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 4248 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 4249 4250 Message.__init__(self) 4251 self.request = request 4252 self.mode = mode 4253 4254 # message forwarding 4255 self.forward_for = forward_for 4256 4257 @staticmethod 4258 def parse(wmsg): 4259 """ 4260 Verifies and parses an unserialized raw message into an actual WAMP message instance. 4261 4262 :param wmsg: The unserialized raw message. 4263 :type wmsg: list 4264 4265 :returns: An instance of this class. 4266 """ 4267 # this should already be verified by WampSerializer.unserialize 4268 assert(len(wmsg) > 0 and wmsg[0] == Cancel.MESSAGE_TYPE) 4269 4270 if len(wmsg) != 3: 4271 raise ProtocolError("invalid message length {0} for CANCEL".format(len(wmsg))) 4272 4273 request = check_or_raise_id(wmsg[1], u"'request' in CANCEL") 4274 options = check_or_raise_extra(wmsg[2], u"'options' in CANCEL") 4275 4276 # options 4277 # 4278 mode = None 4279 forward_for = None 4280 4281 if u'mode' in options: 4282 4283 option_mode = options[u'mode'] 4284 if type(option_mode) != six.text_type: 4285 raise ProtocolError("invalid type {0} for 'mode' option in CANCEL".format(type(option_mode))) 4286 4287 if option_mode not in [Cancel.SKIP, Cancel.KILLNOWAIT, Cancel.KILL]: 4288 raise ProtocolError("invalid value '{0}' for 'mode' option in CANCEL".format(option_mode)) 4289 4290 mode = option_mode 4291 4292 if u'forward_for' in options: 4293 forward_for = options[u'forward_for'] 4294 valid = False 4295 if type(forward_for) == list: 4296 for ff in forward_for: 4297 if type(ff) != dict: 4298 break 4299 if 'session' not in ff or type(ff['session']) not in six.integer_types: 4300 break 4301 if 'authid' not in ff or type(ff['authid']) != six.text_type: 4302 break 4303 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 4304 break 4305 valid = True 4306 4307 if not valid: 4308 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in CANCEL") 4309 4310 obj = Cancel(request, mode=mode, forward_for=forward_for) 4311 4312 return obj 4313 4314 def marshal(self): 4315 """ 4316 Marshal this object into a raw message for subsequent serialization to bytes. 4317 4318 :returns: The serialized raw message. 4319 :rtype: list 4320 """ 4321 options = {} 4322 4323 if self.mode is not None: 4324 options[u'mode'] = self.mode 4325 if self.forward_for is not None: 4326 options[u'forward_for'] = self.forward_for 4327 4328 return [Cancel.MESSAGE_TYPE, self.request, options] 4329 4330 def __str__(self): 4331 """ 4332 Returns string representation of this message. 4333 """ 4334 return u"Cancel(request={0}, mode={1})".format(self.request, self.mode) 4335 4336 4337class Result(Message): 4338 """ 4339 A WAMP ``RESULT`` message. 4340 4341 Formats: 4342 4343 * ``[RESULT, CALL.Request|id, Details|dict]`` 4344 * ``[RESULT, CALL.Request|id, Details|dict, YIELD.Arguments|list]`` 4345 * ``[RESULT, CALL.Request|id, Details|dict, YIELD.Arguments|list, YIELD.ArgumentsKw|dict]`` 4346 * ``[RESULT, CALL.Request|id, Details|dict, Payload|binary]`` 4347 """ 4348 4349 MESSAGE_TYPE = 50 4350 """ 4351 The WAMP message code for this type of message. 4352 """ 4353 4354 __slots__ = ( 4355 'request', 4356 'args', 4357 'kwargs', 4358 'payload', 4359 'progress', 4360 'enc_algo', 4361 'enc_key', 4362 'enc_serializer', 4363 'callee', 4364 'callee_authid', 4365 'callee_authrole', 4366 'forward_for', 4367 ) 4368 4369 def __init__(self, 4370 request, 4371 args=None, 4372 kwargs=None, 4373 payload=None, 4374 progress=None, 4375 enc_algo=None, 4376 enc_key=None, 4377 enc_serializer=None, 4378 callee=None, 4379 callee_authid=None, 4380 callee_authrole=None, 4381 forward_for=None): 4382 """ 4383 4384 :param request: The request ID of the original `CALL` request. 4385 :type request: int 4386 4387 :param args: Positional values for application-defined event payload. 4388 Must be serializable using any serializers in use. 4389 :type args: list or tuple or None 4390 4391 :param kwargs: Keyword values for application-defined event payload. 4392 Must be serializable using any serializers in use. 4393 :type kwargs: dict or None 4394 4395 :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset. 4396 :type payload: bytes or None 4397 4398 :param progress: If ``True``, this result is a progressive call result, and subsequent 4399 results (or a final error) will follow. 4400 :type progress: bool or None 4401 4402 :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload. 4403 :type enc_algo: str or None 4404 4405 :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key. 4406 :type enc_key: str or None 4407 4408 :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload. 4409 :type enc_serializer: str or None 4410 4411 :param callee: The WAMP session ID of the effective callee that responded with the result. Only filled if callee is disclosed. 4412 :type callee: None or int 4413 4414 :param callee_authid: The WAMP authid of the responding callee. Only filled if callee is disclosed. 4415 :type callee_authid: None or unicode 4416 4417 :param callee_authrole: The WAMP authrole of the responding callee. Only filled if callee is disclosed. 4418 :type callee_authrole: None or unicode 4419 4420 :param forward_for: When this Result is forwarded for a client/callee (or from an intermediary router). 4421 :type forward_for: list[dict] 4422 """ 4423 assert(type(request) in six.integer_types) 4424 assert(args is None or type(args) in [list, tuple]) 4425 assert(kwargs is None or type(kwargs) == dict) 4426 assert(payload is None or type(payload) == six.binary_type) 4427 assert(payload is None or (payload is not None and args is None and kwargs is None)) 4428 assert(progress is None or type(progress) == bool) 4429 4430 assert(enc_algo is None or is_valid_enc_algo(enc_algo)) 4431 assert(enc_key is None or type(enc_key) == six.text_type) 4432 assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer)) 4433 assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None)) 4434 4435 assert(callee is None or type(callee) in six.integer_types) 4436 assert(callee_authid is None or type(callee_authid) == six.text_type) 4437 assert(callee_authrole is None or type(callee_authrole) == six.text_type) 4438 4439 assert(forward_for is None or type(forward_for) == list) 4440 if forward_for: 4441 for ff in forward_for: 4442 assert type(ff) == dict 4443 assert 'session' in ff and type(ff['session']) in six.integer_types 4444 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 4445 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 4446 4447 Message.__init__(self) 4448 self.request = request 4449 self.args = args 4450 self.kwargs = _validate_kwargs(kwargs) 4451 self.payload = payload 4452 self.progress = progress 4453 4454 # payload transparency related knobs 4455 self.enc_algo = enc_algo 4456 self.enc_key = enc_key 4457 self.enc_serializer = enc_serializer 4458 4459 # effective callee that responded with the result 4460 self.callee = callee 4461 self.callee_authid = callee_authid 4462 self.callee_authrole = callee_authrole 4463 4464 # message forwarding 4465 self.forward_for = forward_for 4466 4467 @staticmethod 4468 def parse(wmsg): 4469 """ 4470 Verifies and parses an unserialized raw message into an actual WAMP message instance. 4471 4472 :param wmsg: The unserialized raw message. 4473 :type wmsg: list 4474 4475 :returns: An instance of this class. 4476 """ 4477 # this should already be verified by WampSerializer.unserialize 4478 assert(len(wmsg) > 0 and wmsg[0] == Result.MESSAGE_TYPE) 4479 4480 if len(wmsg) not in (3, 4, 5): 4481 raise ProtocolError("invalid message length {0} for RESULT".format(len(wmsg))) 4482 4483 request = check_or_raise_id(wmsg[1], u"'request' in RESULT") 4484 details = check_or_raise_extra(wmsg[2], u"'details' in RESULT") 4485 4486 args = None 4487 kwargs = None 4488 payload = None 4489 progress = None 4490 enc_algo = None 4491 enc_key = None 4492 enc_serializer = None 4493 callee = None 4494 callee_authid = None 4495 callee_authrole = None 4496 forward_for = None 4497 4498 if len(wmsg) == 4 and type(wmsg[3]) in [six.text_type, six.binary_type]: 4499 4500 payload = wmsg[3] 4501 4502 enc_algo = details.get(u'enc_algo', None) 4503 if enc_algo and not is_valid_enc_algo(enc_algo): 4504 raise ProtocolError("invalid value {0} for 'enc_algo' detail in RESULT".format(enc_algo)) 4505 4506 enc_key = details.get(u'enc_key', None) 4507 if enc_key and type(enc_key) != six.text_type: 4508 raise ProtocolError("invalid type {0} for 'enc_key' detail in RESULT".format(type(enc_key))) 4509 4510 enc_serializer = details.get(u'enc_serializer', None) 4511 if enc_serializer and not is_valid_enc_serializer(enc_serializer): 4512 raise ProtocolError("invalid value {0} for 'enc_serializer' detail in RESULT".format(enc_serializer)) 4513 4514 else: 4515 if len(wmsg) > 3: 4516 args = wmsg[3] 4517 if args is not None and type(args) != list: 4518 raise ProtocolError("invalid type {0} for 'args' in RESULT".format(type(args))) 4519 4520 if len(wmsg) > 4: 4521 kwargs = wmsg[4] 4522 if type(kwargs) != dict: 4523 raise ProtocolError("invalid type {0} for 'kwargs' in RESULT".format(type(kwargs))) 4524 4525 if u'progress' in details: 4526 4527 detail_progress = details[u'progress'] 4528 if type(detail_progress) != bool: 4529 raise ProtocolError("invalid type {0} for 'progress' option in RESULT".format(type(detail_progress))) 4530 4531 progress = detail_progress 4532 4533 if u'callee' in details: 4534 4535 detail_callee = details[u'callee'] 4536 if type(detail_callee) not in six.integer_types: 4537 raise ProtocolError("invalid type {0} for 'callee' detail in RESULT".format(type(detail_callee))) 4538 4539 callee = detail_callee 4540 4541 if u'callee_authid' in details: 4542 4543 detail_callee_authid = details[u'callee_authid'] 4544 if type(detail_callee_authid) != six.text_type: 4545 raise ProtocolError("invalid type {0} for 'callee_authid' detail in RESULT".format(type(detail_callee_authid))) 4546 4547 callee_authid = detail_callee_authid 4548 4549 if u'callee_authrole' in details: 4550 4551 detail_callee_authrole = details[u'callee_authrole'] 4552 if type(detail_callee_authrole) != six.text_type: 4553 raise ProtocolError("invalid type {0} for 'callee_authrole' detail in RESULT".format(type(detail_callee_authrole))) 4554 4555 callee_authrole = detail_callee_authrole 4556 4557 if u'forward_for' in details: 4558 forward_for = details[u'forward_for'] 4559 valid = False 4560 if type(forward_for) == list: 4561 for ff in forward_for: 4562 if type(ff) != dict: 4563 break 4564 if 'session' not in ff or type(ff['session']) not in six.integer_types: 4565 break 4566 if 'authid' not in ff or type(ff['authid']) != six.text_type: 4567 break 4568 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 4569 break 4570 valid = True 4571 4572 if not valid: 4573 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in RESULT") 4574 4575 obj = Result(request, 4576 args=args, 4577 kwargs=kwargs, 4578 payload=payload, 4579 progress=progress, 4580 enc_algo=enc_algo, 4581 enc_key=enc_key, 4582 enc_serializer=enc_serializer, 4583 callee=callee, 4584 callee_authid=callee_authid, 4585 callee_authrole=callee_authrole, 4586 forward_for=forward_for) 4587 4588 return obj 4589 4590 def marshal(self): 4591 """ 4592 Marshal this object into a raw message for subsequent serialization to bytes. 4593 4594 :returns: The serialized raw message. 4595 :rtype: list 4596 """ 4597 details = {} 4598 4599 if self.progress is not None: 4600 details[u'progress'] = self.progress 4601 4602 if self.callee is not None: 4603 details[u'callee'] = self.callee 4604 if self.callee_authid is not None: 4605 details[u'callee_authid'] = self.callee_authid 4606 if self.callee_authrole is not None: 4607 details[u'callee_authrole'] = self.callee_authrole 4608 if self.forward_for is not None: 4609 details[u'forward_for'] = self.forward_for 4610 4611 if self.payload: 4612 if self.enc_algo is not None: 4613 details[u'enc_algo'] = self.enc_algo 4614 if self.enc_key is not None: 4615 details[u'enc_key'] = self.enc_key 4616 if self.enc_serializer is not None: 4617 details[u'enc_serializer'] = self.enc_serializer 4618 return [Result.MESSAGE_TYPE, self.request, details, self.payload] 4619 else: 4620 if self.kwargs: 4621 return [Result.MESSAGE_TYPE, self.request, details, self.args, self.kwargs] 4622 elif self.args: 4623 return [Result.MESSAGE_TYPE, self.request, details, self.args] 4624 else: 4625 return [Result.MESSAGE_TYPE, self.request, details] 4626 4627 def __str__(self): 4628 """ 4629 Returns string representation of this message. 4630 """ 4631 return u"Result(request={0}, args={1}, kwargs={2}, progress={3}, enc_algo={4}, enc_key={5}, enc_serializer={6}, payload={7}, callee={8}, callee_authid={9}, callee_authrole={10}, forward_for={11})".format(self.request, self.args, self.kwargs, self.progress, self.enc_algo, self.enc_key, self.enc_serializer, b2a(self.payload), self.callee, self.callee_authid, self.callee_authrole, self.forward_for) 4632 4633 4634class Register(Message): 4635 """ 4636 A WAMP ``REGISTER`` message. 4637 4638 Format: ``[REGISTER, Request|id, Options|dict, Procedure|uri]`` 4639 """ 4640 4641 MESSAGE_TYPE = 64 4642 """ 4643 The WAMP message code for this type of message. 4644 """ 4645 4646 MATCH_EXACT = u'exact' 4647 MATCH_PREFIX = u'prefix' 4648 MATCH_WILDCARD = u'wildcard' 4649 4650 INVOKE_SINGLE = u'single' 4651 INVOKE_FIRST = u'first' 4652 INVOKE_LAST = u'last' 4653 INVOKE_ROUNDROBIN = u'roundrobin' 4654 INVOKE_RANDOM = u'random' 4655 INVOKE_ALL = u'all' 4656 4657 __slots__ = ( 4658 'request', 4659 'procedure', 4660 'match', 4661 'invoke', 4662 'concurrency', 4663 'force_reregister', 4664 'forward_for', 4665 ) 4666 4667 def __init__(self, 4668 request, 4669 procedure, 4670 match=None, 4671 invoke=None, 4672 concurrency=None, 4673 force_reregister=None, 4674 forward_for=None): 4675 """ 4676 4677 :param request: The WAMP request ID of this request. 4678 :type request: int 4679 4680 :param procedure: The WAMP or application URI of the RPC endpoint provided. 4681 :type procedure: str 4682 4683 :param match: The procedure matching policy to be used for the registration. 4684 :type match: str 4685 4686 :param invoke: The procedure invocation policy to be used for the registration. 4687 :type invoke: str 4688 4689 :param concurrency: The (maximum) concurrency to be used for the registration. 4690 :type concurrency: int 4691 4692 :param forward_for: When this Register is forwarded over a router-to-router link, 4693 or via an intermediary router. 4694 :type forward_for: list[dict] 4695 """ 4696 assert(type(request) in six.integer_types) 4697 assert(type(procedure) == six.text_type) 4698 assert(match is None or type(match) == six.text_type) 4699 assert(match is None or match in [Register.MATCH_EXACT, Register.MATCH_PREFIX, Register.MATCH_WILDCARD]) 4700 assert(invoke is None or type(invoke) == six.text_type) 4701 assert(invoke is None or invoke in [Register.INVOKE_SINGLE, Register.INVOKE_FIRST, Register.INVOKE_LAST, Register.INVOKE_ROUNDROBIN, Register.INVOKE_RANDOM]) 4702 assert(concurrency is None or (type(concurrency) in six.integer_types and concurrency > 0)) 4703 assert force_reregister in [None, True, False] 4704 assert(forward_for is None or type(forward_for) == list) 4705 if forward_for: 4706 for ff in forward_for: 4707 assert type(ff) == dict 4708 assert 'session' in ff and type(ff['session']) in six.integer_types 4709 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 4710 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 4711 4712 Message.__init__(self) 4713 self.request = request 4714 self.procedure = procedure 4715 self.match = match or Register.MATCH_EXACT 4716 self.invoke = invoke or Register.INVOKE_SINGLE 4717 self.concurrency = concurrency 4718 self.force_reregister = force_reregister 4719 self.forward_for = forward_for 4720 4721 @staticmethod 4722 def parse(wmsg): 4723 """ 4724 Verifies and parses an unserialized raw message into an actual WAMP message instance. 4725 4726 :param wmsg: The unserialized raw message. 4727 :type wmsg: list 4728 4729 :returns: An instance of this class. 4730 """ 4731 # this should already be verified by WampSerializer.unserialize 4732 assert(len(wmsg) > 0 and wmsg[0] == Register.MESSAGE_TYPE) 4733 4734 if len(wmsg) != 4: 4735 raise ProtocolError("invalid message length {0} for REGISTER".format(len(wmsg))) 4736 4737 request = check_or_raise_id(wmsg[1], u"'request' in REGISTER") 4738 options = check_or_raise_extra(wmsg[2], u"'options' in REGISTER") 4739 4740 match = Register.MATCH_EXACT 4741 invoke = Register.INVOKE_SINGLE 4742 concurrency = None 4743 force_reregister = None 4744 forward_for = None 4745 4746 if u'match' in options: 4747 4748 option_match = options[u'match'] 4749 if type(option_match) != six.text_type: 4750 raise ProtocolError("invalid type {0} for 'match' option in REGISTER".format(type(option_match))) 4751 4752 if option_match not in [Register.MATCH_EXACT, Register.MATCH_PREFIX, Register.MATCH_WILDCARD]: 4753 raise ProtocolError("invalid value {0} for 'match' option in REGISTER".format(option_match)) 4754 4755 match = option_match 4756 4757 if match == Register.MATCH_EXACT: 4758 allow_empty_components = False 4759 allow_last_empty = False 4760 4761 elif match == Register.MATCH_PREFIX: 4762 allow_empty_components = False 4763 allow_last_empty = True 4764 4765 elif match == Register.MATCH_WILDCARD: 4766 allow_empty_components = True 4767 allow_last_empty = False 4768 4769 else: 4770 raise Exception("logic error") 4771 4772 procedure = check_or_raise_uri(wmsg[3], u"'procedure' in REGISTER", allow_empty_components=allow_empty_components, allow_last_empty=allow_last_empty) 4773 4774 if u'invoke' in options: 4775 4776 option_invoke = options[u'invoke'] 4777 if type(option_invoke) != six.text_type: 4778 raise ProtocolError("invalid type {0} for 'invoke' option in REGISTER".format(type(option_invoke))) 4779 4780 if option_invoke not in [Register.INVOKE_SINGLE, Register.INVOKE_FIRST, Register.INVOKE_LAST, Register.INVOKE_ROUNDROBIN, Register.INVOKE_RANDOM]: 4781 raise ProtocolError("invalid value {0} for 'invoke' option in REGISTER".format(option_invoke)) 4782 4783 invoke = option_invoke 4784 4785 if u'concurrency' in options: 4786 4787 options_concurrency = options[u'concurrency'] 4788 if type(options_concurrency) not in six.integer_types: 4789 raise ProtocolError("invalid type {0} for 'concurrency' option in REGISTER".format(type(options_concurrency))) 4790 4791 if options_concurrency < 1: 4792 raise ProtocolError("invalid value {0} for 'concurrency' option in REGISTER".format(options_concurrency)) 4793 4794 concurrency = options_concurrency 4795 4796 options_reregister = options.get(u'force_reregister', None) 4797 if options_reregister not in [True, False, None]: 4798 raise ProtocolError( 4799 "invalid type {0} for 'force_reregister option in REGISTER".format( 4800 type(options_reregister) 4801 ) 4802 ) 4803 if options_reregister is not None: 4804 force_reregister = options_reregister 4805 4806 if u'forward_for' in options: 4807 forward_for = options[u'forward_for'] 4808 valid = False 4809 if type(forward_for) == list: 4810 for ff in forward_for: 4811 if type(ff) != dict: 4812 break 4813 if 'session' not in ff or type(ff['session']) not in six.integer_types: 4814 break 4815 if 'authid' not in ff or type(ff['authid']) != six.text_type: 4816 break 4817 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 4818 break 4819 valid = True 4820 4821 if not valid: 4822 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in REGISTER") 4823 4824 obj = Register(request, procedure, match=match, invoke=invoke, concurrency=concurrency, 4825 force_reregister=force_reregister, forward_for=forward_for) 4826 4827 return obj 4828 4829 def marshal_options(self): 4830 options = {} 4831 4832 if self.match and self.match != Register.MATCH_EXACT: 4833 options[u'match'] = self.match 4834 4835 if self.invoke and self.invoke != Register.INVOKE_SINGLE: 4836 options[u'invoke'] = self.invoke 4837 4838 if self.concurrency: 4839 options[u'concurrency'] = self.concurrency 4840 4841 if self.force_reregister is not None: 4842 options[u'force_reregister'] = self.force_reregister 4843 4844 if self.forward_for is not None: 4845 options[u'forward_for'] = self.forward_for 4846 4847 return options 4848 4849 def marshal(self): 4850 """ 4851 Marshal this object into a raw message for subsequent serialization to bytes. 4852 4853 :returns: The serialized raw message. 4854 :rtype: list 4855 """ 4856 return [Register.MESSAGE_TYPE, self.request, self.marshal_options(), self.procedure] 4857 4858 def __str__(self): 4859 """ 4860 Returns string representation of this message. 4861 """ 4862 return u"Register(request={0}, procedure={1}, match={2}, invoke={3}, concurrency={4}, force_reregister={5}, forward_for={6})".format(self.request, self.procedure, self.match, self.invoke, self.concurrency, self.force_reregister, self.forward_for) 4863 4864 4865class Registered(Message): 4866 """ 4867 A WAMP ``REGISTERED`` message. 4868 4869 Format: ``[REGISTERED, REGISTER.Request|id, Registration|id]`` 4870 """ 4871 4872 MESSAGE_TYPE = 65 4873 """ 4874 The WAMP message code for this type of message. 4875 """ 4876 4877 __slots__ = ( 4878 'request', 4879 'registration', 4880 ) 4881 4882 def __init__(self, request, registration): 4883 """ 4884 4885 :param request: The request ID of the original ``REGISTER`` request. 4886 :type request: int 4887 4888 :param registration: The registration ID for the registered procedure (or procedure pattern). 4889 :type registration: int 4890 """ 4891 assert(type(request) in six.integer_types) 4892 assert(type(registration) in six.integer_types) 4893 4894 Message.__init__(self) 4895 self.request = request 4896 self.registration = registration 4897 4898 @staticmethod 4899 def parse(wmsg): 4900 """ 4901 Verifies and parses an unserialized raw message into an actual WAMP message instance. 4902 4903 :param wmsg: The unserialized raw message. 4904 :type wmsg: list 4905 4906 :returns: An instance of this class. 4907 """ 4908 # this should already be verified by WampSerializer.unserialize 4909 assert(len(wmsg) > 0 and wmsg[0] == Registered.MESSAGE_TYPE) 4910 4911 if len(wmsg) != 3: 4912 raise ProtocolError("invalid message length {0} for REGISTERED".format(len(wmsg))) 4913 4914 request = check_or_raise_id(wmsg[1], u"'request' in REGISTERED") 4915 registration = check_or_raise_id(wmsg[2], u"'registration' in REGISTERED") 4916 4917 obj = Registered(request, registration) 4918 4919 return obj 4920 4921 def marshal(self): 4922 """ 4923 Marshal this object into a raw message for subsequent serialization to bytes. 4924 4925 :returns: The serialized raw message. 4926 :rtype: list 4927 """ 4928 return [Registered.MESSAGE_TYPE, self.request, self.registration] 4929 4930 def __str__(self): 4931 """ 4932 Returns string representation of this message. 4933 """ 4934 return u"Registered(request={0}, registration={1})".format(self.request, self.registration) 4935 4936 4937class Unregister(Message): 4938 """ 4939 A WAMP `UNREGISTER` message. 4940 4941 Formats: 4942 4943 * ``[UNREGISTER, Request|id, REGISTERED.Registration|id]`` 4944 * ``[UNREGISTER, Request|id, REGISTERED.Registration|id, Options|dict]`` 4945 """ 4946 4947 MESSAGE_TYPE = 66 4948 """ 4949 The WAMP message code for this type of message. 4950 """ 4951 4952 __slots__ = ( 4953 'request', 4954 'registration', 4955 'forward_for', 4956 ) 4957 4958 def __init__(self, request, registration, forward_for=None): 4959 """ 4960 4961 :param request: The WAMP request ID of this request. 4962 :type request: int 4963 4964 :param registration: The registration ID for the registration to unregister. 4965 :type registration: int 4966 4967 :param forward_for: When this Unregister is forwarded over a router-to-router link, 4968 or via an intermediary router. 4969 :type forward_for: list[dict] 4970 """ 4971 assert(type(request) in six.integer_types) 4972 assert(type(registration) in six.integer_types) 4973 4974 Message.__init__(self) 4975 self.request = request 4976 self.registration = registration 4977 self.forward_for = forward_for 4978 4979 @staticmethod 4980 def parse(wmsg): 4981 """ 4982 Verifies and parses an unserialized raw message into an actual WAMP message instance. 4983 4984 :param wmsg: The unserialized raw message. 4985 :type wmsg: list 4986 4987 :returns: An instance of this class. 4988 """ 4989 # this should already be verified by WampSerializer.unserialize 4990 assert(len(wmsg) > 0 and wmsg[0] == Unregister.MESSAGE_TYPE) 4991 4992 if len(wmsg) not in [3, 4]: 4993 raise ProtocolError("invalid message length {0} for WAMP UNREGISTER".format(len(wmsg))) 4994 4995 request = check_or_raise_id(wmsg[1], u"'request' in UNREGISTER") 4996 registration = check_or_raise_id(wmsg[2], u"'registration' in UNREGISTER") 4997 4998 options = None 4999 if len(wmsg) > 3: 5000 options = check_or_raise_extra(wmsg[3], u"'options' in UNREGISTER") 5001 5002 forward_for = None 5003 if options and u'forward_for' in options: 5004 forward_for = options[u'forward_for'] 5005 valid = False 5006 if type(forward_for) == list: 5007 for ff in forward_for: 5008 if type(ff) != dict: 5009 break 5010 if 'session' not in ff or type(ff['session']) not in six.integer_types: 5011 break 5012 if 'authid' not in ff or type(ff['authid']) != six.text_type: 5013 break 5014 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 5015 break 5016 valid = True 5017 5018 if not valid: 5019 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in UNREGISTER") 5020 5021 obj = Unregister(request, registration, forward_for=forward_for) 5022 5023 return obj 5024 5025 def marshal(self): 5026 """ 5027 Marshal this object into a raw message for subsequent serialization to bytes. 5028 5029 :returns: The serialized raw message. 5030 :rtype: list 5031 """ 5032 if self.forward_for: 5033 options = { 5034 u'forward_for': self.forward_for, 5035 } 5036 return [Unregister.MESSAGE_TYPE, self.request, self.registration, options] 5037 else: 5038 return [Unregister.MESSAGE_TYPE, self.request, self.registration] 5039 5040 def __str__(self): 5041 """ 5042 Returns string representation of this message. 5043 """ 5044 return u"Unregister(request={0}, registration={1})".format(self.request, self.registration) 5045 5046 5047class Unregistered(Message): 5048 """ 5049 A WAMP ``UNREGISTERED`` message. 5050 5051 Formats: 5052 5053 * ``[UNREGISTERED, UNREGISTER.Request|id]`` 5054 * ``[UNREGISTERED, UNREGISTER.Request|id, Details|dict]`` 5055 """ 5056 5057 MESSAGE_TYPE = 67 5058 """ 5059 The WAMP message code for this type of message. 5060 """ 5061 5062 __slots__ = ( 5063 'request', 5064 'registration', 5065 'reason', 5066 ) 5067 5068 def __init__(self, request, registration=None, reason=None): 5069 """ 5070 5071 :param request: The request ID of the original ``UNREGISTER`` request. 5072 :type request: int 5073 5074 :param registration: If unregister was actively triggered by router, the ID 5075 of the registration revoked. 5076 :type registration: int or None 5077 5078 :param reason: The reason (an URI) for revocation. 5079 :type reason: str or None. 5080 """ 5081 assert(type(request) in six.integer_types) 5082 assert(registration is None or type(registration) in six.integer_types) 5083 assert(reason is None or type(reason) == six.text_type) 5084 assert((request != 0 and registration is None) or (request == 0 and registration != 0)) 5085 5086 Message.__init__(self) 5087 self.request = request 5088 self.registration = registration 5089 self.reason = reason 5090 5091 @staticmethod 5092 def parse(wmsg): 5093 """ 5094 Verifies and parses an unserialized raw message into an actual WAMP message instance. 5095 5096 :param wmsg: The unserialized raw message. 5097 :type wmsg: list 5098 5099 :returns: An instance of this class. 5100 """ 5101 # this should already be verified by WampSerializer.unserialize 5102 assert(len(wmsg) > 0 and wmsg[0] == Unregistered.MESSAGE_TYPE) 5103 5104 if len(wmsg) not in [2, 3]: 5105 raise ProtocolError("invalid message length {0} for UNREGISTERED".format(len(wmsg))) 5106 5107 request = check_or_raise_id(wmsg[1], u"'request' in UNREGISTERED") 5108 5109 registration = None 5110 reason = None 5111 5112 if len(wmsg) > 2: 5113 5114 details = check_or_raise_extra(wmsg[2], u"'details' in UNREGISTERED") 5115 5116 if u"registration" in details: 5117 details_registration = details[u"registration"] 5118 if type(details_registration) not in six.integer_types: 5119 raise ProtocolError("invalid type {0} for 'registration' detail in UNREGISTERED".format(type(details_registration))) 5120 registration = details_registration 5121 5122 if u"reason" in details: 5123 reason = check_or_raise_uri(details[u"reason"], u"'reason' in UNREGISTERED") 5124 5125 obj = Unregistered(request, registration, reason) 5126 5127 return obj 5128 5129 def marshal(self): 5130 """ 5131 Marshal this object into a raw message for subsequent serialization to bytes. 5132 5133 :returns: The serialized raw message. 5134 :rtype: list 5135 """ 5136 if self.reason is not None or self.registration is not None: 5137 details = {} 5138 if self.reason is not None: 5139 details[u"reason"] = self.reason 5140 if self.registration is not None: 5141 details[u"registration"] = self.registration 5142 return [Unregistered.MESSAGE_TYPE, self.request, details] 5143 else: 5144 return [Unregistered.MESSAGE_TYPE, self.request] 5145 5146 def __str__(self): 5147 """ 5148 Returns string representation of this message. 5149 """ 5150 return u"Unregistered(request={0}, reason={1}, registration={2})".format(self.request, self.reason, self.registration) 5151 5152 5153class Invocation(Message): 5154 """ 5155 A WAMP ``INVOCATION`` message. 5156 5157 Formats: 5158 5159 * ``[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict]`` 5160 * ``[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, CALL.Arguments|list]`` 5161 * ``[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, CALL.Arguments|list, CALL.ArgumentsKw|dict]`` 5162 * ``[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, Payload|binary]`` 5163 """ 5164 5165 MESSAGE_TYPE = 68 5166 """ 5167 The WAMP message code for this type of message. 5168 """ 5169 5170 __slots__ = ( 5171 'request', 5172 'registration', 5173 'args', 5174 'kwargs', 5175 'payload', 5176 'timeout', 5177 'receive_progress', 5178 'caller', 5179 'caller_authid', 5180 'caller_authrole', 5181 'procedure', 5182 'enc_algo', 5183 'enc_key', 5184 'enc_serializer', 5185 'forward_for', 5186 ) 5187 5188 def __init__(self, 5189 request, 5190 registration, 5191 args=None, 5192 kwargs=None, 5193 payload=None, 5194 timeout=None, 5195 receive_progress=None, 5196 caller=None, 5197 caller_authid=None, 5198 caller_authrole=None, 5199 procedure=None, 5200 enc_algo=None, 5201 enc_key=None, 5202 enc_serializer=None, 5203 forward_for=None): 5204 """ 5205 5206 :param request: The WAMP request ID of this request. 5207 :type request: int 5208 5209 :param registration: The registration ID of the endpoint to be invoked. 5210 :type registration: int 5211 5212 :param args: Positional values for application-defined event payload. 5213 Must be serializable using any serializers in use. 5214 :type args: list or tuple or None 5215 5216 :param kwargs: Keyword values for application-defined event payload. 5217 Must be serializable using any serializers in use. 5218 :type kwargs: dict or None 5219 5220 :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset. 5221 :type payload: bytes or None 5222 5223 :param timeout: If present, let the callee automatically cancels 5224 the invocation after this ms. 5225 :type timeout: int or None 5226 5227 :param receive_progress: Indicates if the callee should produce progressive results. 5228 :type receive_progress: bool or None 5229 5230 :param caller: The WAMP session ID of the caller. Only filled if caller is disclosed. 5231 :type caller: None or int 5232 5233 :param caller_authid: The WAMP authid of the caller. Only filled if caller is disclosed. 5234 :type caller_authid: None or unicode 5235 5236 :param caller_authrole: The WAMP authrole of the caller. Only filled if caller is disclosed. 5237 :type caller_authrole: None or unicode 5238 5239 :param procedure: For pattern-based registrations, the invocation MUST include the actual procedure being called. 5240 :type procedure: str or None 5241 5242 :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload. 5243 :type enc_algo: str or None 5244 5245 :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key. 5246 :type enc_key: str or None 5247 5248 :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload. 5249 :type enc_serializer: str or None 5250 5251 :param forward_for: When this Call is forwarded for a client (or from an intermediary router). 5252 :type forward_for: list[dict] 5253 """ 5254 assert(type(request) in six.integer_types) 5255 assert(type(registration) in six.integer_types) 5256 assert(args is None or type(args) in [list, tuple]) 5257 assert(kwargs is None or type(kwargs) == dict) 5258 assert(payload is None or type(payload) == six.binary_type) 5259 assert(payload is None or (payload is not None and args is None and kwargs is None)) 5260 assert(timeout is None or type(timeout) in six.integer_types) 5261 assert(receive_progress is None or type(receive_progress) == bool) 5262 assert(caller is None or type(caller) in six.integer_types) 5263 assert(caller_authid is None or type(caller_authid) == six.text_type) 5264 assert(caller_authrole is None or type(caller_authrole) == six.text_type) 5265 assert(procedure is None or type(procedure) == six.text_type) 5266 assert(enc_algo is None or is_valid_enc_algo(enc_algo)) 5267 assert(enc_key is None or type(enc_key) == six.text_type) 5268 assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer)) 5269 assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None)) 5270 5271 assert(forward_for is None or type(forward_for) == list) 5272 if forward_for: 5273 for ff in forward_for: 5274 assert type(ff) == dict 5275 assert 'session' in ff and type(ff['session']) in six.integer_types 5276 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 5277 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 5278 5279 Message.__init__(self) 5280 self.request = request 5281 self.registration = registration 5282 self.args = args 5283 self.kwargs = _validate_kwargs(kwargs) 5284 self.payload = payload 5285 self.timeout = timeout 5286 self.receive_progress = receive_progress 5287 self.caller = caller 5288 self.caller_authid = caller_authid 5289 self.caller_authrole = caller_authrole 5290 self.procedure = procedure 5291 self.enc_algo = enc_algo 5292 self.enc_key = enc_key 5293 self.enc_serializer = enc_serializer 5294 5295 # message forwarding 5296 self.forward_for = forward_for 5297 5298 @staticmethod 5299 def parse(wmsg): 5300 """ 5301 Verifies and parses an unserialized raw message into an actual WAMP message instance. 5302 5303 :param wmsg: The unserialized raw message. 5304 :type wmsg: list 5305 5306 :returns: An instance of this class. 5307 """ 5308 # this should already be verified by WampSerializer.unserialize 5309 assert(len(wmsg) > 0 and wmsg[0] == Invocation.MESSAGE_TYPE) 5310 5311 if len(wmsg) not in (4, 5, 6): 5312 raise ProtocolError("invalid message length {0} for INVOCATION".format(len(wmsg))) 5313 5314 request = check_or_raise_id(wmsg[1], u"'request' in INVOCATION") 5315 registration = check_or_raise_id(wmsg[2], u"'registration' in INVOCATION") 5316 details = check_or_raise_extra(wmsg[3], u"'details' in INVOCATION") 5317 5318 args = None 5319 kwargs = None 5320 payload = None 5321 enc_algo = None 5322 enc_key = None 5323 enc_serializer = None 5324 5325 if len(wmsg) == 5 and type(wmsg[4]) == six.binary_type: 5326 5327 payload = wmsg[4] 5328 5329 enc_algo = details.get(u'enc_algo', None) 5330 if enc_algo and not is_valid_enc_algo(enc_algo): 5331 raise ProtocolError("invalid value {0} for 'enc_algo' detail in INVOCATION".format(enc_algo)) 5332 5333 enc_key = details.get(u'enc_key', None) 5334 if enc_key and type(enc_key) != six.text_type: 5335 raise ProtocolError("invalid type {0} for 'enc_key' detail in INVOCATION".format(type(enc_key))) 5336 5337 enc_serializer = details.get(u'enc_serializer', None) 5338 if enc_serializer and not is_valid_enc_serializer(enc_serializer): 5339 raise ProtocolError("invalid value {0} for 'enc_serializer' detail in INVOCATION".format(enc_serializer)) 5340 5341 else: 5342 if len(wmsg) > 4: 5343 args = wmsg[4] 5344 if args is not None and type(args) != list: 5345 raise ProtocolError("invalid type {0} for 'args' in INVOCATION".format(type(args))) 5346 5347 if len(wmsg) > 5: 5348 kwargs = wmsg[5] 5349 if type(kwargs) != dict: 5350 raise ProtocolError("invalid type {0} for 'kwargs' in INVOCATION".format(type(kwargs))) 5351 5352 timeout = None 5353 receive_progress = None 5354 caller = None 5355 caller_authid = None 5356 caller_authrole = None 5357 procedure = None 5358 forward_for = None 5359 5360 if u'timeout' in details: 5361 5362 detail_timeout = details[u'timeout'] 5363 if type(detail_timeout) not in six.integer_types: 5364 raise ProtocolError("invalid type {0} for 'timeout' detail in INVOCATION".format(type(detail_timeout))) 5365 5366 if detail_timeout < 0: 5367 raise ProtocolError("invalid value {0} for 'timeout' detail in INVOCATION".format(detail_timeout)) 5368 5369 timeout = detail_timeout 5370 5371 if u'receive_progress' in details: 5372 5373 detail_receive_progress = details[u'receive_progress'] 5374 if type(detail_receive_progress) != bool: 5375 raise ProtocolError("invalid type {0} for 'receive_progress' detail in INVOCATION".format(type(detail_receive_progress))) 5376 5377 receive_progress = detail_receive_progress 5378 5379 if u'caller' in details: 5380 5381 detail_caller = details[u'caller'] 5382 if type(detail_caller) not in six.integer_types: 5383 raise ProtocolError("invalid type {0} for 'caller' detail in INVOCATION".format(type(detail_caller))) 5384 5385 caller = detail_caller 5386 5387 if u'caller_authid' in details: 5388 5389 detail_caller_authid = details[u'caller_authid'] 5390 if type(detail_caller_authid) != six.text_type: 5391 raise ProtocolError("invalid type {0} for 'caller_authid' detail in INVOCATION".format(type(detail_caller_authid))) 5392 5393 caller_authid = detail_caller_authid 5394 5395 if u'caller_authrole' in details: 5396 5397 detail_caller_authrole = details[u'caller_authrole'] 5398 if type(detail_caller_authrole) != six.text_type: 5399 raise ProtocolError("invalid type {0} for 'caller_authrole' detail in INVOCATION".format(type(detail_caller_authrole))) 5400 5401 caller_authrole = detail_caller_authrole 5402 5403 if u'procedure' in details: 5404 5405 detail_procedure = details[u'procedure'] 5406 if type(detail_procedure) != six.text_type: 5407 raise ProtocolError("invalid type {0} for 'procedure' detail in INVOCATION".format(type(detail_procedure))) 5408 5409 procedure = detail_procedure 5410 5411 if u'forward_for' in details: 5412 forward_for = details[u'forward_for'] 5413 valid = False 5414 if type(forward_for) == list: 5415 for ff in forward_for: 5416 if type(ff) != dict: 5417 break 5418 if 'session' not in ff or type(ff['session']) not in six.integer_types: 5419 break 5420 if 'authid' not in ff or type(ff['authid']) != six.text_type: 5421 break 5422 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 5423 break 5424 valid = True 5425 5426 if not valid: 5427 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in INVOCATION") 5428 5429 obj = Invocation(request, 5430 registration, 5431 args=args, 5432 kwargs=kwargs, 5433 payload=payload, 5434 timeout=timeout, 5435 receive_progress=receive_progress, 5436 caller=caller, 5437 caller_authid=caller_authid, 5438 caller_authrole=caller_authrole, 5439 procedure=procedure, 5440 enc_algo=enc_algo, 5441 enc_key=enc_key, 5442 enc_serializer=enc_serializer, 5443 forward_for=forward_for) 5444 5445 return obj 5446 5447 def marshal(self): 5448 """ 5449 Marshal this object into a raw message for subsequent serialization to bytes. 5450 5451 :returns: The serialized raw message. 5452 :rtype: list 5453 """ 5454 options = {} 5455 5456 if self.timeout is not None: 5457 options[u'timeout'] = self.timeout 5458 5459 if self.receive_progress is not None: 5460 options[u'receive_progress'] = self.receive_progress 5461 5462 if self.caller is not None: 5463 options[u'caller'] = self.caller 5464 5465 if self.caller_authid is not None: 5466 options[u'caller_authid'] = self.caller_authid 5467 5468 if self.caller_authrole is not None: 5469 options[u'caller_authrole'] = self.caller_authrole 5470 5471 if self.procedure is not None: 5472 options[u'procedure'] = self.procedure 5473 5474 if self.forward_for is not None: 5475 options[u'forward_for'] = self.forward_for 5476 5477 if self.payload: 5478 if self.enc_algo is not None: 5479 options[u'enc_algo'] = self.enc_algo 5480 if self.enc_key is not None: 5481 options[u'enc_key'] = self.enc_key 5482 if self.enc_serializer is not None: 5483 options[u'enc_serializer'] = self.enc_serializer 5484 return [Invocation.MESSAGE_TYPE, self.request, self.registration, options, self.payload] 5485 else: 5486 if self.kwargs: 5487 return [Invocation.MESSAGE_TYPE, self.request, self.registration, options, self.args, self.kwargs] 5488 elif self.args: 5489 return [Invocation.MESSAGE_TYPE, self.request, self.registration, options, self.args] 5490 else: 5491 return [Invocation.MESSAGE_TYPE, self.request, self.registration, options] 5492 5493 def __str__(self): 5494 """ 5495 Returns string representation of this message. 5496 """ 5497 return u"Invocation(request={0}, registration={1}, args={2}, kwargs={3}, timeout={4}, receive_progress={5}, caller={6}, caller_authid={7}, caller_authrole={8}, procedure={9}, enc_algo={10}, enc_key={11}, enc_serializer={12}, payload={13})".format(self.request, self.registration, self.args, self.kwargs, self.timeout, self.receive_progress, self.caller, self.caller_authid, self.caller_authrole, self.procedure, self.enc_algo, self.enc_key, self.enc_serializer, b2a(self.payload)) 5498 5499 5500class Interrupt(Message): 5501 """ 5502 A WAMP ``INTERRUPT`` message. 5503 5504 Format: ``[INTERRUPT, INVOCATION.Request|id, Options|dict]`` 5505 5506 See: https://wamp-proto.org/static/rfc/draft-oberstet-hybi-crossbar-wamp.html#rfc.section.14.3.4 5507 """ 5508 5509 MESSAGE_TYPE = 69 5510 """ 5511 The WAMP message code for this type of message. 5512 """ 5513 5514 KILL = u'kill' 5515 KILLNOWAIT = u'killnowait' 5516 5517 __slots__ = ( 5518 'request', 5519 'mode', 5520 'reason', 5521 'forward_for', 5522 ) 5523 5524 def __init__(self, request, mode=None, reason=None, forward_for=None): 5525 """ 5526 5527 :param request: The WAMP request ID of the original ``INVOCATION`` to interrupt. 5528 :type request: int 5529 5530 :param mode: Specifies how to interrupt the invocation (``"killnowait"`` or ``"kill"``). 5531 With ``"kill"``, the router will wait for the callee to return an ERROR before 5532 proceeding (sending back an ERROR to the original caller). With ``"killnowait"`` the 5533 router will immediately proceed (on the caller side returning an ERROR) - but still 5534 expects the callee to send an ERROR to conclude the message exchange for the inflight 5535 call. 5536 :type mode: str or None 5537 5538 :param reason: The reason (an URI) for the invocation interrupt, eg actively 5539 triggered by the caller (``"wamp.error.canceled"`` - ApplicationError.CANCELED) or 5540 passively because of timeout (``"wamp.error.timeout"`` - ApplicationError.TIMEOUT). 5541 :type reason: str or None. 5542 5543 :param forward_for: When this Call is forwarded for a client (or from an intermediary router). 5544 :type forward_for: list[dict] 5545 """ 5546 assert(type(request) in six.integer_types) 5547 assert(mode is None or type(mode) == six.text_type) 5548 assert(mode is None or mode in [self.KILL, self.KILLNOWAIT]) 5549 assert(reason is None or type(reason) == six.text_type) 5550 5551 assert(forward_for is None or type(forward_for) == list) 5552 if forward_for: 5553 for ff in forward_for: 5554 assert type(ff) == dict 5555 assert 'session' in ff and type(ff['session']) in six.integer_types 5556 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 5557 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 5558 5559 Message.__init__(self) 5560 self.request = request 5561 self.mode = mode 5562 self.reason = reason 5563 5564 # message forwarding 5565 self.forward_for = forward_for 5566 5567 @staticmethod 5568 def parse(wmsg): 5569 """ 5570 Verifies and parses an unserialized raw message into an actual WAMP message instance. 5571 5572 :param wmsg: The unserialized raw message. 5573 :type wmsg: list 5574 5575 :returns: An instance of this class. 5576 """ 5577 # this should already be verified by WampSerializer.unserialize 5578 assert(len(wmsg) > 0 and wmsg[0] == Interrupt.MESSAGE_TYPE) 5579 5580 if len(wmsg) != 3: 5581 raise ProtocolError("invalid message length {0} for INTERRUPT".format(len(wmsg))) 5582 5583 request = check_or_raise_id(wmsg[1], u"'request' in INTERRUPT") 5584 options = check_or_raise_extra(wmsg[2], u"'options' in INTERRUPT") 5585 5586 # options 5587 # 5588 mode = None 5589 reason = None 5590 forward_for = None 5591 5592 if u'mode' in options: 5593 5594 option_mode = options[u'mode'] 5595 if type(option_mode) != six.text_type: 5596 raise ProtocolError("invalid type {0} for 'mode' option in INTERRUPT".format(type(option_mode))) 5597 5598 if option_mode not in [Interrupt.KILL, Interrupt.KILLNOWAIT]: 5599 raise ProtocolError("invalid value '{0}' for 'mode' option in INTERRUPT".format(option_mode)) 5600 5601 mode = option_mode 5602 5603 if u'reason' in options: 5604 reason = check_or_raise_uri(options[u'reason'], u'"reason" in INTERRUPT') 5605 5606 if u'forward_for' in options: 5607 forward_for = options[u'forward_for'] 5608 valid = False 5609 if type(forward_for) == list: 5610 for ff in forward_for: 5611 if type(ff) != dict: 5612 break 5613 if 'session' not in ff or type(ff['session']) not in six.integer_types: 5614 break 5615 if 'authid' not in ff or type(ff['authid']) != six.text_type: 5616 break 5617 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 5618 break 5619 valid = True 5620 5621 if not valid: 5622 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in INTERRUPT") 5623 5624 obj = Interrupt(request, mode=mode, reason=reason, forward_for=forward_for) 5625 5626 return obj 5627 5628 def marshal(self): 5629 """ 5630 Marshal this object into a raw message for subsequent serialization to bytes. 5631 5632 :returns: The serialized raw message. 5633 :rtype: list 5634 """ 5635 options = {} 5636 5637 if self.mode is not None: 5638 options[u'mode'] = self.mode 5639 5640 if self.reason is not None: 5641 options[u'reason'] = self.reason 5642 5643 if self.forward_for is not None: 5644 options[u'forward_for'] = self.forward_for 5645 5646 return [Interrupt.MESSAGE_TYPE, self.request, options] 5647 5648 def __str__(self): 5649 """ 5650 Returns string representation of this message. 5651 """ 5652 return u"Interrupt(request={0}, mode={1}, reason={2})".format(self.request, self.mode, self.reason) 5653 5654 5655class Yield(Message): 5656 """ 5657 A WAMP ``YIELD`` message. 5658 5659 Formats: 5660 5661 * ``[YIELD, INVOCATION.Request|id, Options|dict]`` 5662 * ``[YIELD, INVOCATION.Request|id, Options|dict, Arguments|list]`` 5663 * ``[YIELD, INVOCATION.Request|id, Options|dict, Arguments|list, ArgumentsKw|dict]`` 5664 * ``[YIELD, INVOCATION.Request|id, Options|dict, Payload|binary]`` 5665 """ 5666 5667 MESSAGE_TYPE = 70 5668 """ 5669 The WAMP message code for this type of message. 5670 """ 5671 5672 __slots__ = ( 5673 'request', 5674 'args', 5675 'kwargs', 5676 'payload', 5677 'progress', 5678 'enc_algo', 5679 'enc_key', 5680 'enc_serializer', 5681 'callee', 5682 'callee_authid', 5683 'callee_authrole', 5684 'forward_for', 5685 ) 5686 5687 def __init__(self, 5688 request, 5689 args=None, 5690 kwargs=None, 5691 payload=None, 5692 progress=None, 5693 enc_algo=None, 5694 enc_key=None, 5695 enc_serializer=None, 5696 callee=None, 5697 callee_authid=None, 5698 callee_authrole=None, 5699 forward_for=None): 5700 """ 5701 5702 :param request: The WAMP request ID of the original call. 5703 :type request: int 5704 5705 :param args: Positional values for application-defined event payload. 5706 Must be serializable using any serializers in use. 5707 :type args: list or tuple or None 5708 5709 :param kwargs: Keyword values for application-defined event payload. 5710 Must be serializable using any serializers in use. 5711 :type kwargs: dict or None 5712 5713 :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset. 5714 :type payload: bytes or None 5715 5716 :param progress: If ``True``, this result is a progressive invocation result, and subsequent 5717 results (or a final error) will follow. 5718 :type progress: bool or None 5719 5720 :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload. 5721 :type enc_algo: str or None 5722 5723 :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key. 5724 :type enc_key: str or None 5725 5726 :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload. 5727 :type enc_serializer: str or None 5728 5729 :param callee: The WAMP session ID of the effective callee that responded with the error. Only filled if callee is disclosed. 5730 :type callee: None or int 5731 5732 :param callee_authid: The WAMP authid of the responding callee. Only filled if callee is disclosed. 5733 :type callee_authid: None or unicode 5734 5735 :param callee_authrole: The WAMP authrole of the responding callee. Only filled if callee is disclosed. 5736 :type callee_authrole: None or unicode 5737 5738 :param forward_for: When this Call is forwarded for a client (or from an intermediary router). 5739 :type forward_for: list[dict] 5740 """ 5741 assert(type(request) in six.integer_types) 5742 assert(args is None or type(args) in [list, tuple]) 5743 assert(kwargs is None or type(kwargs) == dict) 5744 assert(payload is None or type(payload) == six.binary_type) 5745 assert(payload is None or (payload is not None and args is None and kwargs is None)) 5746 assert(progress is None or type(progress) == bool) 5747 assert(enc_algo is None or is_valid_enc_algo(enc_algo)) 5748 assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None)) 5749 assert(enc_key is None or type(enc_key) == six.text_type) 5750 assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer)) 5751 5752 assert(callee is None or type(callee) in six.integer_types) 5753 assert(callee_authid is None or type(callee_authid) == six.text_type) 5754 assert(callee_authrole is None or type(callee_authrole) == six.text_type) 5755 5756 assert(forward_for is None or type(forward_for) == list) 5757 if forward_for: 5758 for ff in forward_for: 5759 assert type(ff) == dict 5760 assert 'session' in ff and type(ff['session']) in six.integer_types 5761 assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == six.text_type) 5762 assert 'authrole' in ff and type(ff['authrole']) == six.text_type 5763 5764 Message.__init__(self) 5765 self.request = request 5766 self.args = args 5767 self.kwargs = _validate_kwargs(kwargs) 5768 self.payload = payload 5769 self.progress = progress 5770 self.enc_algo = enc_algo 5771 self.enc_key = enc_key 5772 self.enc_serializer = enc_serializer 5773 5774 # effective callee that responded with the result 5775 self.callee = callee 5776 self.callee_authid = callee_authid 5777 self.callee_authrole = callee_authrole 5778 5779 # message forwarding 5780 self.forward_for = forward_for 5781 5782 @staticmethod 5783 def parse(wmsg): 5784 """ 5785 Verifies and parses an unserialized raw message into an actual WAMP message instance. 5786 5787 :param wmsg: The unserialized raw message. 5788 :type wmsg: list 5789 5790 :returns: An instance of this class. 5791 """ 5792 # this should already be verified by WampSerializer.unserialize 5793 assert(len(wmsg) > 0 and wmsg[0] == Yield.MESSAGE_TYPE) 5794 5795 if len(wmsg) not in (3, 4, 5): 5796 raise ProtocolError("invalid message length {0} for YIELD".format(len(wmsg))) 5797 5798 request = check_or_raise_id(wmsg[1], u"'request' in YIELD") 5799 options = check_or_raise_extra(wmsg[2], u"'options' in YIELD") 5800 5801 args = None 5802 kwargs = None 5803 payload = None 5804 enc_algo = None 5805 enc_key = None 5806 enc_serializer = None 5807 5808 if len(wmsg) == 4 and type(wmsg[3]) == six.binary_type: 5809 5810 payload = wmsg[3] 5811 5812 enc_algo = options.get(u'enc_algo', None) 5813 if enc_algo and not is_valid_enc_algo(enc_algo): 5814 raise ProtocolError("invalid value {0} for 'enc_algo' detail in YIELD".format(enc_algo)) 5815 5816 enc_key = options.get(u'enc_key', None) 5817 if enc_key and type(enc_key) != six.text_type: 5818 raise ProtocolError("invalid type {0} for 'enc_key' detail in YIELD".format(type(enc_key))) 5819 5820 enc_serializer = options.get(u'enc_serializer', None) 5821 if enc_serializer and not is_valid_enc_serializer(enc_serializer): 5822 raise ProtocolError("invalid value {0} for 'enc_serializer' detail in YIELD".format(enc_serializer)) 5823 5824 else: 5825 if len(wmsg) > 3: 5826 args = wmsg[3] 5827 if args is not None and type(args) != list: 5828 raise ProtocolError("invalid type {0} for 'args' in YIELD".format(type(args))) 5829 5830 if len(wmsg) > 4: 5831 kwargs = wmsg[4] 5832 if type(kwargs) != dict: 5833 raise ProtocolError("invalid type {0} for 'kwargs' in YIELD".format(type(kwargs))) 5834 5835 progress = None 5836 callee = None 5837 callee_authid = None 5838 callee_authrole = None 5839 forward_for = None 5840 5841 if u'progress' in options: 5842 5843 option_progress = options[u'progress'] 5844 if type(option_progress) != bool: 5845 raise ProtocolError("invalid type {0} for 'progress' option in YIELD".format(type(option_progress))) 5846 5847 progress = option_progress 5848 5849 if u'callee' in options: 5850 5851 option_callee = options[u'callee'] 5852 if type(option_callee) not in six.integer_types: 5853 raise ProtocolError("invalid type {0} for 'callee' detail in YIELD".format(type(option_callee))) 5854 5855 callee = option_callee 5856 5857 if u'callee_authid' in options: 5858 5859 option_callee_authid = options[u'callee_authid'] 5860 if type(option_callee_authid) != six.text_type: 5861 raise ProtocolError("invalid type {0} for 'callee_authid' detail in YIELD".format(type(option_callee_authid))) 5862 5863 callee_authid = option_callee_authid 5864 5865 if u'callee_authrole' in options: 5866 5867 option_callee_authrole = options[u'callee_authrole'] 5868 if type(option_callee_authrole) != six.text_type: 5869 raise ProtocolError("invalid type {0} for 'callee_authrole' detail in YIELD".format(type(option_callee_authrole))) 5870 5871 callee_authrole = option_callee_authrole 5872 5873 if u'forward_for' in options: 5874 forward_for = options[u'forward_for'] 5875 valid = False 5876 if type(forward_for) == list: 5877 for ff in forward_for: 5878 if type(ff) != dict: 5879 break 5880 if 'session' not in ff or type(ff['session']) not in six.integer_types: 5881 break 5882 if 'authid' not in ff or type(ff['authid']) != six.text_type: 5883 break 5884 if 'authrole' not in ff or type(ff['authrole']) != six.text_type: 5885 break 5886 valid = True 5887 5888 if not valid: 5889 raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in YIELD") 5890 5891 obj = Yield(request, 5892 args=args, 5893 kwargs=kwargs, 5894 payload=payload, 5895 progress=progress, 5896 enc_algo=enc_algo, 5897 enc_key=enc_key, 5898 enc_serializer=enc_serializer, 5899 callee=callee, 5900 callee_authid=callee_authid, 5901 callee_authrole=callee_authrole, 5902 forward_for=forward_for) 5903 5904 return obj 5905 5906 def marshal(self): 5907 """ 5908 Marshal this object into a raw message for subsequent serialization to bytes. 5909 5910 :returns: The serialized raw message. 5911 :rtype: list 5912 """ 5913 options = {} 5914 5915 if self.progress is not None: 5916 options[u'progress'] = self.progress 5917 5918 if self.callee is not None: 5919 options[u'callee'] = self.callee 5920 if self.callee_authid is not None: 5921 options[u'callee_authid'] = self.callee_authid 5922 if self.callee_authrole is not None: 5923 options[u'callee_authrole'] = self.callee_authrole 5924 if self.forward_for is not None: 5925 options[u'forward_for'] = self.forward_for 5926 5927 if self.payload: 5928 if self.enc_algo is not None: 5929 options[u'enc_algo'] = self.enc_algo 5930 if self.enc_key is not None: 5931 options[u'enc_key'] = self.enc_key 5932 if self.enc_serializer is not None: 5933 options[u'enc_serializer'] = self.enc_serializer 5934 return [Yield.MESSAGE_TYPE, self.request, options, self.payload] 5935 else: 5936 if self.kwargs: 5937 return [Yield.MESSAGE_TYPE, self.request, options, self.args, self.kwargs] 5938 elif self.args: 5939 return [Yield.MESSAGE_TYPE, self.request, options, self.args] 5940 else: 5941 return [Yield.MESSAGE_TYPE, self.request, options] 5942 5943 def __str__(self): 5944 """ 5945 Returns string representation of this message. 5946 """ 5947 return u"Yield(request={0}, args={1}, kwargs={2}, progress={3}, enc_algo={4}, enc_key={5}, enc_serializer={6}, payload={7}, callee={8}, callee_authid={9}, callee_authrole={10}, forward_for={11})".format(self.request, self.args, self.kwargs, self.progress, self.enc_algo, self.enc_key, self.enc_serializer, b2a(self.payload), self.callee, self.callee_authid, self.callee_authrole, self.forward_for) 5948