def convert(self, value, param, ctx): try: parse_url(value) except Exception as e: self.fail(style_error(str(e))) else: return value
def test_parse_url08(self): self.assertEqual( parse_url("wss://localhost/ws?foo=bar&moo=23&moo=44"), (True, 'localhost', 443, '/ws?foo=bar&moo=23&moo=44', '/ws', { 'moo': ['23', '44'], 'foo': ['bar'] }))
def do_check_for_activation_file(activation_file, reboot_on_discover): if os.path.isfile(activation_file): tags = _parse_activation_file(activation_file) is_secure, hostname, port, _, _, _ = parse_url(tags['management-url']) config = { 'type': 'websocket', 'url': tags['management-url'], 'endpoint': { 'type': 'tcp', 'host': hostname, 'port': port, 'timeout': 5 } } if is_secure: config['endpoint']['tls'] = { 'hostname': hostname } _msg = 'Found auto-activation file "{}", using management URL "{}" - [1]'.format(activation_file, config['url']) log.info(click.style(_msg, fg='red', bold=True)) if reboot_on_discover and fn_reboot: # stop the node and enforce complete reboot - which will then pick up the new configuration fn_reboot() # return the management uplink transport configuration, as derived from the activation file return config
def start_session(url, realm, extra): from autobahn.wamp.types import ComponentConfig from autobahn.websocket.util import parse_url from autobahn.asyncio.websocket import WampWebSocketClientFactory loop = asyncio.get_event_loop() ready = asyncio.Event() @asyncio.coroutine def connected(session): ready.set() if not extra: extra = {} extra['loop'] = loop extra['connected'] = connected session = [None] def create(): cfg = ComponentConfig(realm, extra) session[0] = ClientSession(cfg) return session[0] transport_factory = WampWebSocketClientFactory(create, url=url) _, host, port, _, _, _ = parse_url(url) coro = loop.create_connection(transport_factory, host, port) (transport, protocol) = loop.run_until_complete(coro) loop.run_until_complete(ready.wait()) return session[0]
def setupService(self): """ Setup the application component. """ is_secure, host, port, resource, path, params = parse_url(self.url) # factory for use ApplicationSession def create(): cfg = ComponentConfig(self.realm, self.extra) session = self.make(cfg) return session # create a WAMP-over-WebSocket transport client factory transport_factory = self.factory(create, url=self.url) # setup the client from a Twisted endpoint if is_secure: from twisted.application.internet import SSLClient ctx = self.context_factory if ctx is None: from twisted.internet.ssl import optionsForClientTLS ctx = optionsForClientTLS(host) client = SSLClient(host, port, transport_factory, contextFactory=ctx) else: if self.context_factory is not None: raise Exception("context_factory specified on non-secure URI") from twisted.application.internet import TCPClient client = TCPClient(host, port, transport_factory) client.setServiceParent(self)
def setupService(self): """ Setup the application component. """ is_secure, host, port, resource, path, params = parse_url(self.url) # factory for use ApplicationSession def create(): cfg = ComponentConfig(self.realm, self.extra) session = self.make(cfg) return session # create a WAMP-over-WebSocket transport client factory transport_factory = self.factory(create, url=self.url) # setup the client from a Twisted endpoint if is_secure: from twisted.application.internet import SSLClient ctx = self.context_factory if ctx is None: from twisted.internet.ssl import optionsForClientTLS ctx = optionsForClientTLS(host) client = SSLClient(host, port, transport_factory, contextFactory=ctx) else: if self.context_factory is not None: raise Exception( "context_factory specified on non-secure URI") from twisted.application.internet import TCPClient client = TCPClient(host, port, transport_factory) client.setServiceParent(self)
def run(self): is_secure, host, port, resource, path, params = (parse_url(self.url)) self.reactor.connectSSL(host=host, port=port, factory=self.factory, contextFactory=self.ssl_context) self.reactor.run()
def run(self, make): """ Run the application component. :param make: A factory that produces instances of :class:`autobahn.asyncio.wamp.ApplicationSession` when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`. :type make: callable """ is_secure, host, port, resource, path, params = parse_url(self.url) # start logging to console if self.debug or self.debug_wamp or self.debug_app: pass # log.startLogging(sys.stdout) # factory for use ApplicationSession def create(): """ Will trying to create an ApplicationSession object :return: ApplicationSession """ cfg = ComponentConfig(self.realm, self.extra) try: session = make(config=cfg) except Exception as e: # the app component could not be created .. fatal self.logger.critical('CREATE RUNNER EXCEPTION {}'.format(e.message)) else: session.debug_app = self.debug_app return session # create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory( create, url=self.url ) if is_secure: endpoint_descriptor = "ssl:{0}:{1}".format(host, port) else: endpoint_descriptor = "tcp:{0}:{1}".format(host, port) try: self.logger.info('Trying to connect to: {}'.format(endpoint_descriptor)) self.connect(endpoint_descriptor, transport_factory, make) return self except Exception as e: self.logger.error('CLIENT CONNECT ERROR: {}'.format(e.message))
async def _try_connect(self): isSecure, host, port, resource, path, params = parse_url(self._url) transport_factory = MyFactory(self._create, url=self._url, serializers=None) return await self._loop.create_connection(transport_factory, host, port, ssl=False)
def make_ws_app(c): is_secure, host, port, resource, path, params = (parse_url( c('conf')['websocket']['url'])) return c('loop').create_server( protocol_factory=c('factory'), host=host, port=port, ssl=c('secure_context'), )
def run(self, make): """ Run the application component. :param make: A factory that produces instances of :class:`autobahn.asyncio.wamp.ApplicationSession` when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`. :type make: callable """ is_secure, host, port, resource, path, params = parse_url(self.url) # start logging to console if self.debug or self.debug_wamp or self.debug_app: pass # log.startLogging(sys.stdout) # factory for use ApplicationSession def create(): """ Will trying to create an ApplicationSession object :return: ApplicationSession """ cfg = ComponentConfig(self.realm, self.extra) try: session = make(config=cfg) except Exception as e: # the app component could not be created .. fatal self.logger.critical('CREATE RUNNER EXCEPTION {}'.format( e.message)) else: session.debug_app = self.debug_app return session # create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory(create, url=self.url) if is_secure: endpoint_descriptor = "ssl:{0}:{1}".format(host, port) else: endpoint_descriptor = "tcp:{0}:{1}".format(host, port) try: self.logger.info( 'Trying to connect to: {}'.format(endpoint_descriptor)) self.connect(endpoint_descriptor, transport_factory, make) return self except Exception as e: self.logger.error('CLIENT CONNECT ERROR: {}'.format(e.message))
def create_management_session(url='wss://master.xbr.network/ws', realm='com.crossbario.fabric', privkey_file='default.priv'): txaio.start_logging(level='info') privkey_file = os.path.join(os.path.expanduser('~/.crossbar'), privkey_file) # for authenticating the management client, we need a Ed25519 public/private key pair # here, we are reusing the user key - so this needs to exist before privkey_hex = None user_id = None if not os.path.exists(privkey_file): raise Exception( 'private key file {} does not exist'.format(privkey_file)) else: with open(privkey_file, 'r') as f: data = f.read() for line in data.splitlines(): if line.startswith('private-key-ed25519'): privkey_hex = line.split(':')[1].strip() if line.startswith('user-id'): user_id = line.split(':')[1].strip() if privkey_hex is None: raise Exception('no private key found in keyfile!') if user_id is None: raise Exception('no user ID found in keyfile!') url_is_secure, _, _, _, _, _ = parse_url(url) key = cryptosign.CryptosignKey.from_bytes(binascii.a2b_hex(privkey_hex)) extra = { 'key': key, 'authid': user_id, 'ready': Deferred(), 'return_code': None, 'command': CmdListManagementRealms(), # WAMP-cryptosign TLS channel binding 'channel_binding': 'tls-unique' if url_is_secure else None, } runner = ApplicationRunner(url=url, realm=realm, extra=extra) runner.run(ManagementClientSession, start_reactor=False) return extra['ready']
def __init__(self, url, realm, extra=None, serializers=None, debug=False, debug_wamp=False, debug_app=False, ssl=None, loop=None, retry_strategy=PersistentStrategy()): """ :param url: The WebSocket URL of the WAMP router to connect to (e.g. `ws://somehost.com:8090/somepath`) :type url: unicode :param realm: The WAMP realm to join the application session to. :type realm: unicode :param extra: Optional extra configuration to forward to the application component. :type extra: dict :param serializers: A list of WAMP serializers to use (or None for default serializers). Serializers must implement :class:`autobahn.wamp.interfaces.ISerializer`. :type serializers: list :param debug: Turn on low-level debugging. :type debug: bool :param debug_wamp: Turn on WAMP-level debugging. :type debug_wamp: bool :param debug_app: Turn on app-level debugging. :type debug_app: bool :param ssl: An (optional) SSL context instance or a bool. See the documentation for the `loop.create_connection` asyncio method, to which this value is passed as the ``ssl=`` kwarg. :type ssl: :class:`ssl.SSLContext` or bool """ self.url = url self.realm = realm self.extra = extra or dict() self.debug = debug self.debug_wamp = debug_wamp self.debug_app = debug_app self.serializers = serializers self.loop = loop or asyncio.get_event_loop() self.retry_strategy = retry_strategy self.closing = False self.transport_factory = None self.active_protocol = None self.isSecure, self.host, self.port, _, _, _ = parse_url(url) if ssl is None: self.ssl = self.isSecure else: if ssl and not self.isSecure: raise RuntimeError( 'ssl argument value passed to %s conflicts with the "ws:" ' 'prefix of the url argument. Did you mean to use "wss:"?' % self.__class__.__name__) self.ssl = ssl
def wamp_configured(cls): "get host, port & ssl from wmp_url" (isSecureURL, host, port, resource, path, params) = parse_url(cls.wmp_url) cls._host = host cls._port = port if not getattr(cls, "wmp_ssl", None): cls._ssl = isSecureURL elif cls.wmp_ssl and not isSecureURL: raise RuntimeError( 'the wmp_ssl class variable of %s conflicts with the "ws:" prefix ' 'of the wmp_url variable. Did you mean to use "wss:"?' % cls.__name__) else: cls._ssl = cls.wmp_ssl return cls
def run(self, make): # 1) factory for use ApplicationSession def create(): cfg = ComponentConfig(self.realm, self.extra) try: session = make(cfg) except Exception as e: # the app component could not be created .. fatal print(e) asyncio.get_event_loop().stop() else: session.debug_app = self.debug_app return session isSecure, host, port, resource, path, params = parse_url(self.url) if self.ssl is None: ssl = isSecure else: if self.ssl and not isSecure: raise RuntimeError( 'ssl argument value passed to %s conflicts with the "ws:" ' 'prefix of the url argument. Did you mean to use "wss:"?' % self.__class__.__name__) ssl = self.ssl # 2) create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory(create, url=self.url, serializers=self.serializers) # transport_factory.setProtocolOptions(failByDrop=False, openHandshakeTimeout=90, closeHandshakeTimeout=5) # 3) start the client loop = asyncio.get_event_loop() txaio.use_asyncio() txaio.config.loop = loop coro = loop.create_connection(transport_factory, host, port, ssl=ssl) (transport, protocol) = loop.run_until_complete(coro) try: loop.add_signal_handler(signal.SIGTERM, loop.stop) except NotImplementedError: # signals are not available on Windows pass
def _normalize_transport(transport, check_native_endpoint=None): """ Check a WAMP connecting transport configuration, and add any defaults that we can. These are: - type: websocket - endpoint: if not specified, fill in from URL """ if type(transport) != dict: raise RuntimeError( 'invalid type {} for transport configuration - must be a dict'. format(type(transport))) if 'type' not in transport: transport['type'] = 'websocket' if transport['type'] not in ['websocket', 'rawsocket']: raise RuntimeError('invalid transport type {}'.format( transport['type'])) if transport['type'] == 'websocket': if 'url' not in transport: raise ValueError("Missing 'url' in transport") if 'endpoint' not in transport: is_secure, host, port, resource, path, params = parse_url( transport['url']) transport['endpoint'] = { 'type': 'tcp', 'host': host, 'port': port, 'tls': False if not is_secure else dict(hostname=host), } _normalize_endpoint(transport['endpoint'], check_native_endpoint) # XXX can/should we check e.g. serializers here? elif transport['type'] == 'rawsocket': if 'endpoint' not in transport: raise ValueError("Missing 'endpoint' in transport") else: assert False, 'should not arrive here'
def _normalize_transport(transport, check_native_endpoint=None): """ Check a WAMP connecting transport configuration, and add any defaults that we can. These are: - type: websocket - endpoint: if not specified, fill in from URL """ if type(transport) != dict: raise RuntimeError('invalid type {} for transport configuration - must be a dict'.format(type(transport))) if 'type' not in transport: transport['type'] = 'websocket' if transport['type'] not in ['websocket', 'rawsocket']: raise RuntimeError('invalid transport type {}'.format(transport['type'])) if transport['type'] == 'websocket': if 'url' not in transport: raise ValueError("Missing 'url' in transport") if 'endpoint' not in transport: is_secure, host, port, resource, path, params = parse_url(transport['url']) transport['endpoint'] = { 'type': 'tcp', 'host': host, 'port': port, 'tls': False if not is_secure else dict(hostname=host), } _normalize_endpoint(transport['endpoint'], check_native_endpoint) # XXX can/should we check e.g. serializers here? elif transport['type'] == 'rawsocket': if 'endpoint' not in transport: raise ValueError("Missing 'endpoint' in transport") else: assert False, 'should not arrive here'
def _normalize_transport(transport, check_native_endpoint=None): """ Check a WAMP connecting transport configuration, and add any defaults that we can. These are: - type: websocket - endpoint: if not specified, fill in from URL """ if type(transport) != dict: raise RuntimeError("invalid type {} for transport configuration - must be a dict".format(type(transport))) if "type" not in transport: transport["type"] = "websocket" if transport["type"] not in ["websocket", "rawsocket"]: raise RuntimeError("invalid transport type {}".format(transport["type"])) if transport["type"] == "websocket": if "url" not in transport: raise ValueError("Missing 'url' in transport") if "endpoint" not in transport: is_secure, host, port, resource, path, params = parse_url(transport["url"]) transport["endpoint"] = { "type": "tcp", "host": host, "port": port, "tls": False if not is_secure else dict(hostname=host), } _normalize_endpoint(transport["endpoint"], check_native_endpoint) # XXX can/should we check e.g. serializers here? elif transport["type"] == "rawsocket": if "endpoint" not in transport: raise ValueError("Missing 'endpoint' in transport") else: assert False, "should not arrive here"
def _start(self): if self.session: return import asyncio import txaio txaio.use_asyncio() txaio.config.loop = self.loop = asyncio.get_event_loop() from autobahn.wamp import protocol from autobahn.wamp.types import ComponentConfig from autobahn.websocket.util import parse_url from autobahn.asyncio.websocket import WampWebSocketClientFactory from ..remote.client import ClientSession @asyncio.coroutine def _connected(session): self.ready.set() def create(): cfg = ComponentConfig(self.realm, { 'loop': self.loop, 'func': _connected, }) self.session = ClientSession(cfg) return self.session transport_factory = WampWebSocketClientFactory(create, url=self.url) _, host, port, _, _, _ = parse_url(self.url) self.ready = asyncio.Event() coro = self.loop.create_connection(transport_factory, host, port) (transport, protocol) = self.loop.run_until_complete(coro) print(transport, protocol) self.loop.run_until_complete(self.ready.wait())
def __init__(self, main=None, setup=None, transports=None, config=None): """ :param main: A callable that runs user code for the component. The component will be started with a "main-like" procedure. When a transport has been connected and a session has been established and joined a realm, the user code will be run until it finishes which signals that the component has run to completion. :type main: callable :param setup: A callable that runs user code for the component. The component will be started with a "setup-like" procedure. When a transport has been connected and a session has been established and joined a realm, the user code will be run until it finishes which signals that the component is now "ready". The component will continue to run until it explicitly closes the session or the underlying transport closes. :type setup: callable :param transports: Transport configurations for creating transports. :type transports: None or unicode or list :param config: Session configuration. :type config: None or dict """ ObservableMixin.__init__(self) if main is None and setup is None: raise RuntimeError('either a "main" or "setup" procedure must be provided for a component') if main is not None and setup is not None: raise RuntimeError('either a "main" or "setup" procedure must be provided for a component (not both)') if main is not None and not callable(main): raise RuntimeError('"main" must be a callable if given') if setup is not None and not callable(setup): raise RuntimeError('"setup" must be a callable if given') if setup: self._entry = setup self._entry_type = Component.TYPE_SETUP elif main: self._entry = main self._entry_type = Component.TYPE_MAIN else: assert(False), 'logic error' # use WAMP-over-WebSocket to localhost when no transport is specified at all if transports is None: transports = u'ws://127.0.0.1:8080/ws' # allows to provide an URL instead of a list of transports if type(transports) == six.text_type: url = transports is_secure, host, port, resource, path, params = parse_url(url) transport = { 'type': 'websocket', 'url': url, 'endpoint': { 'type': 'tcp', 'host': host, 'port': port } } if is_secure: # FIXME transport['endpoint']['tls'] = {} transports = [transport] # now check and save list of transports self._transports = [] idx = 0 for transport in transports: check_transport(transport) self._transports.append(Transport(idx, transport)) idx += 1 self._realm = u'realm1' self._extra = None
def _create_transport(index, transport, check_native_endpoint=None): """ Internal helper to insert defaults and create _Transport instances. :param transport: a (possibly valid) transport configuration :type transport: dict :returns: a _Transport instance :raises: ValueError on invalid configuration """ if type(transport) != dict: raise ValueError('invalid type {} for transport configuration - must be a dict'.format(type(transport))) valid_transport_keys = [ 'type', 'url', 'endpoint', 'serializer', 'serializers', 'options', 'max_retries', 'max_retry_delay', 'initial_retry_delay', 'retry_delay_growth', 'retry_delay_jitter', ] for k in transport.keys(): if k not in valid_transport_keys: raise ValueError( "'{}' is not a valid configuration item".format(k) ) kind = 'websocket' if 'type' in transport: if transport['type'] not in ['websocket', 'rawsocket']: raise ValueError('Invalid transport type {}'.format(transport['type'])) kind = transport['type'] else: transport['type'] = 'websocket' options = dict() if 'options' in transport: options = transport['options'] if not isinstance(options, dict): raise ValueError( 'options must be a dict, not {}'.format(type(options)) ) if kind == 'websocket': for key in ['url']: if key not in transport: raise ValueError("Transport requires '{}' key".format(key)) # endpoint not required; we will deduce from URL if it's not provided # XXX not in the branch I rebased; can this go away? (is it redundant??) if 'endpoint' not in transport: is_secure, host, port, resource, path, params = parse_url(transport['url']) endpoint_config = { 'type': 'tcp', 'host': host, 'port': port, 'tls': is_secure, } else: # note: we're avoiding mutating the incoming "configuration" # dict, so this should avoid that too... endpoint_config = transport['endpoint'] _validate_endpoint(endpoint_config, check_native_endpoint) if 'serializer' in transport: raise ValueError("'serializer' is only for rawsocket; use 'serializers'") if 'serializers' in transport: if not isinstance(transport['serializers'], (list, tuple)): raise ValueError("'serializers' must be a list of strings") if not all([ isinstance(s, (six.text_type, str)) for s in transport['serializers']]): raise ValueError("'serializers' must be a list of strings") valid_serializers = ('msgpack', 'json') for serial in transport['serializers']: if serial not in valid_serializers: raise ValueError( "Invalid serializer '{}' (expected one of: {})".format( serial, ', '.join([repr(s) for s in valid_serializers]), ) ) serializer_config = transport.get('serializers', [u'msgpack', u'json']) elif kind == 'rawsocket': if 'endpoint' not in transport: raise ValueError("Missing 'endpoint' in transport") endpoint_config = transport['endpoint'] if 'serializers' in transport: raise ValueError("'serializers' is only for websocket; use 'serializer'") # always a list; len == 1 for rawsocket if 'serializer' in transport: if not isinstance(transport['serializer'], (six.text_type, str)): raise ValueError("'serializer' must be a string") serializer_config = [transport['serializer']] else: serializer_config = [u'msgpack'] else: assert False, 'should not arrive here' kw = {} for key in ['max_retries', 'max_retry_delay', 'initial_retry_delay', 'retry_delay_growth', 'retry_delay_jitter']: if key in transport: kw[key] = transport[key] return _Transport( index, kind=kind, url=transport['url'], endpoint=endpoint_config, serializers=serializer_config, options=options, **kw )
def resetDelay(self): ReconnectingClientFactory.resetDelay(self) self.failed_counter = 0 # create a WAMP application session factory component_config = types.ComponentConfig(realm=CROSSBAR_REALM) client = MyComponent(component_config) # Hack to have only one Client inst. and no client_factory def _get_session(): return client # You can set a custom URL by setting the env. variable WAMP_URL. # e.g. WAMP_URL=ws://localhost:8091 python main.py transport_factory = MyClientFactory(_get_session, url=WAMP_URL) transport_factory.setProtocolOptions(autoPingInterval=10, autoPingTimeout=60) # Setup proper logging from Autobahn import txaio txaio.use_twisted() txaio.config.loop = reactor # txaio.start_logging(level='debug') # start the client from a Twisted endpoint isSecure, host, port, resource, path, params = parse_url(WAMP_URL) transport_factory.host = host transport_factory.port = port WAMP_connector = websocket.connectWS(transport_factory, timeout=5) reactor.run()
def __init__(self, main=None, setup=None, transports=None, config=None): """ :param main: A callable that runs user code for the component. The component will be started with a "main-like" procedure. When a transport has been connected and a session has been established and joined a realm, the user code will be run until it finishes which signals that the component has run to completion. :type main: callable :param setup: A callable that runs user code for the component. The component will be started with a "setup-like" procedure. When a transport has been connected and a session has been established and joined a realm, the user code will be run until it finishes which signals that the component is now "ready". The component will continue to run until it explicitly closes the session or the underlying transport closes. :type setup: callable :param transports: Transport configurations for creating transports. :type transports: None or unicode or list :param config: Session configuration. :type config: None or dict """ ObservableMixin.__init__(self) if main is None and setup is None: raise RuntimeError( 'either a "main" or "setup" procedure must be provided for a component' ) if main is not None and setup is not None: raise RuntimeError( 'either a "main" or "setup" procedure must be provided for a component (not both)' ) if main is not None and not callable(main): raise RuntimeError('"main" must be a callable if given') if setup is not None and not callable(setup): raise RuntimeError('"setup" must be a callable if given') if setup: self._entry = setup self._entry_type = Component.TYPE_SETUP elif main: self._entry = main self._entry_type = Component.TYPE_MAIN else: assert (False), 'logic error' # use WAMP-over-WebSocket to localhost when no transport is specified at all if transports is None: transports = u'ws://127.0.0.1:8080/ws' # allows to provide an URL instead of a list of transports if type(transports) == six.text_type: url = transports is_secure, host, port, resource, path, params = parse_url(url) transport = { 'type': 'websocket', 'url': url, 'endpoint': { 'type': 'tcp', 'host': host, 'port': port } } if is_secure: # FIXME transport['endpoint']['tls'] = {} transports = [transport] # now check and save list of transports self._transports = [] idx = 0 for transport in transports: check_transport(transport) self._transports.append(Transport(idx, transport)) idx += 1 self._realm = u'realm1' self._extra = None
def test_parse_url02(self): self.assertEqual(parse_url("ws://localhost:80"), (False, 'localhost', 80, '/', '/', {}))
def test_parse_url05(self): self.assertEqual(parse_url("wss://localhost/ws"), (True, 'localhost', 443, '/ws', '/ws', {}))
def run(self, make): """ Run the application component. :param make: A factory that produces instances of :class:`autobahn.asyncio.wamp.ApplicationSession` when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`. :type make: callable """ # 1) factory for use ApplicationSession def create(): cfg = ComponentConfig(self.realm, self.extra) try: session = make(cfg) except Exception as e: self.log.error( 'ApplicationSession could not be instantiated: {}'.format( e)) loop = asyncio.get_event_loop() if loop.is_running(): loop.stop() raise else: return session isSecure, host, port, resource, path, params = parse_url(self.url) if self.ssl is None: ssl = isSecure else: if self.ssl and not isSecure: raise RuntimeError( 'ssl argument value passed to %s conflicts with the "ws:" ' 'prefix of the url argument. Did you mean to use "wss:"?' % self.__class__.__name__) ssl = self.ssl # 2) create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory( create, url=self.url, serializers=self.serializers) # 3) start the client loop = asyncio.get_event_loop() txaio.use_asyncio() txaio.config.loop = loop coro = loop.create_connection(transport_factory, host, port, ssl=ssl) (transport, protocol) = loop.run_until_complete(coro) # start logging txaio.start_logging(level='info') try: loop.add_signal_handler(signal.SIGTERM, loop.stop) except NotImplementedError: # signals are not available on Windows pass # 4) now enter the asyncio event loop try: loop.run_forever() except KeyboardInterrupt: # wait until we send Goodbye if user hit ctrl-c # (done outside this except so SIGTERM gets the same handling) pass # give Goodbye message a chance to go through, if we still # have an active session if protocol._session: loop.run_until_complete(protocol._session.leave()) loop.close()
def run_context(self, ctx, command=None): # cfg contains the command lines options and arguments that # click collected for us cfg = ctx.obj cmd = ctx.command.name self.log.info('{klass}.run_context: running shell command "{cmd}"', klass=self.__class__.__name__, cmd=cmd) yes_to_all = cfg.yes_to_all if hasattr(cfg, 'yes_to_all') else False # if cmd not in ['auth', 'shell']: # raise click.ClickException('"{}" command can only be run in shell'.format(cmd)) if self._output_verbosity == Application.OUTPUT_VERBOSITY_VERBOSE: click.echo('Crossbar.io Shell: {}'.format( style_ok('v{}'.format(__version__)))) # load user profile and key for given profile name key, profile = self.load_profile(dotdir=cfg.dotdir, profile=cfg.profile, yes_to_all=yes_to_all, verbose=(ctx.command.name == 'init')) if ctx.command.name == 'init': return # set the Fabric URL to connect to from the profile or default url = profile.url or 'wss://fabric.crossbario.com' # users always authenticate with the user_id from the key, which # filled from the email the user provided authid = key.user_id # the realm can be set from command line, env var, the profile # or can be None, which means the user will be joined to the global # Crossbar.io users realm ('com.crossbario.fabric') realm = cfg.realm or profile.realm or None # the authrole can be set from command line, env var, the profile # or can be None, in which case the role is chosen automatically # from the list of roles the user us authorized for authrole = cfg.role or profile.role or None # this will be fired when the ShellClient below actually has joined # the respective realm on Crossbar.io (either the global users # realm, or a management realm the user has a role on) done = txaio.create_future() url_is_secure, _, _, _, _, _ = parse_url(url) extra = { # these are forward on the actual client connection 'authid': authid, 'authrole': authrole, # these are native Python object and only used client-side 'key': key.key, 'done': done, 'command': command, # WAMP-cryptosign authentication: TLS channel binding 'channel_binding': 'tls-unique' if url_is_secure else None, } cert_options = None if profile.tls_hostname: self.log.info( 'Setting up TLS context (server CA/intermediate certificates, etc) from profile:' ) tls_config = { 'hostname': profile.tls_hostname, 'ca_certificates': profile.tls_certificates } cert_options = _create_tls_client_context(tls_config, '.crossbar', self.log) # for the "auth" command, forward additional command line options if ctx.command.name == 'auth': # user provides authentication code to verify extra['activation_code'] = cfg.code # user requests sending of a new authentication code (while an old one is still pending) extra['request_new_activation_code'] = cfg.new_code # this is the WAMP ApplicationSession that connects the CLI to Crossbar.io self.session = client.ShellClient(ComponentConfig(realm, extra)) runner = ApplicationRunner(url, realm, ssl=cert_options) if self._output_verbosity == Application.OUTPUT_VERBOSITY_VERBOSE: click.echo('Connecting to {} ..'.format(url)) connect_done = runner.run(self.session, start_reactor=False) def on_connect_success(res): self.log.info('{klass}.on_connect_success(res={res})', klass=self.__class__.__name__, res=pformat(res)) def on_connect_error(err): self.log.warn('{klass}.on_connect_error(err={err})', klass=self.__class__.__name__, err=err) if isinstance(err, Failure): err = err.value txaio.reject(done, err) # raise SystemExit(1) txaio.add_callbacks(connect_done, on_connect_success, on_connect_error) def on_success(res): self.log.info('{klass}.on_success(res={res})', klass=self.__class__.__name__, res=pformat(res)) session_details, result = res if cmd == 'auth': self._print_welcome(url, session_details) elif cmd == 'shell': # click.clear() self._print_welcome(url, session_details) # FIXME: # prompt_kwargs = { # 'history': self._history, # } # # from crossbar.shell import repl # # shell_task = loop.create_task( # repl.repl( # ctx, # get_bottom_toolbar_tokens=self._get_bottom_toolbar_tokens, # # get_prompt_tokens=self._get_prompt_tokens, # style=self._style, # prompt_kwargs=prompt_kwargs)) # # try: # loop.run_until_complete(shell_task) # except Exception as e: # print(e) else: if result: self._output_result(result) def on_error(err): self.log.warn('{klass}.on_error(err={err})', klass=self.__class__.__name__, err=err) if isinstance(err, Failure): err = err.value if isinstance(err, ApplicationError): self.log.warn('{message} - {error}', message=err.args[0] if err.args else '', error=err.error) # some ApplicationErrors are actually signaling progress # in the authentication flow, some are real errors exit_code = None if err.error.startswith('fabric.auth-failed.'): error = err.error.split('.')[2] message = err.args[0] if error == 'new-user-auth-code-sent': click.echo( '\nThanks for registering! {}'.format(message)) click.echo( style_ok( 'Please check your inbox and run "crossbar shell auth --code <THE CODE YOU GOT BY EMAIL>.\n' )) elif error == 'registered-user-auth-code-sent': click.echo('\nWelcome back! {}'.format(message)) click.echo( style_ok( 'Please check your inbox and run "crossbar shell auth --code <THE CODE YOU GOT BY EMAIL>.\n' )) elif error == 'pending-activation': click.echo() click.echo(style_ok(message)) click.echo() click.echo( 'Tip: to activate, run "crossbar shell auth --code <THE CODE YOU GOT BY EMAIL>"' ) click.echo( 'Tip: you can request sending a new code with "crossbar shell auth --new-code"' ) click.echo() elif error == 'no-pending-activation': exit_code = 1 click.echo() click.echo( style_error('{} [{}]'.format(message, err.error))) click.echo() elif error == 'email-failure': exit_code = 1 click.echo() click.echo( style_error('{} [{}]'.format(message, err.error))) click.echo() elif error == 'invalid-activation-code': exit_code = 1 click.echo() click.echo( style_error('{} [{}]'.format(message, err.error))) click.echo() else: exit_code = 1 click.echo(style_error('{}'.format(error))) click.echo(style_error(message)) elif err.error.startswith('crossbar.error.'): error = err.error.split('.')[2] message = err.args[0] if error == 'invalid_configuration': click.echo() click.echo( style_error('{} [{}]'.format(message, err.error))) click.echo() else: exit_code = 1 click.echo( style_error('{} [{}]'.format(message, err.error))) else: click.echo(style_error('{}'.format(err))) exit_code = 1 if exit_code: raise SystemExit(exit_code) else: click.echo(style_error('{}'.format(err))) raise SystemExit(1) txaio.add_callbacks(done, on_success, on_error) def doit(reactor): return done react(doit)
def reset_url(self): websocket_uri, url, port = self.link.get_url() self.url = websocket_uri (self.isSecure, self.host, self.port, self.resource, self.path, self.params) = parse_url(websocket_uri)
def run(self, make, start_reactor=True): """ Run the application component. :param make: A factory that produces instances of :class:`autobahn.asyncio.wamp.ApplicationSession` when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`. :type make: callable :param start_reactor: if True (the default) this method starts the Twisted reactor and doesn't return until the reactor stops. If there are any problems starting the reactor or connect()-ing, we stop the reactor and raise the exception back to the caller. :returns: None is returned, unless you specify ``start_reactor=False`` in which case the Deferred that connect() returns is returned; this will callback() with an IProtocol instance, which will actually be an instance of :class:`WampWebSocketClientProtocol` """ if start_reactor: # only select framework, set loop and start logging when we are asked # start the reactor - otherwise we are running in a program that likely # already tool care of all this. from twisted.internet import reactor txaio.use_twisted() txaio.config.loop = reactor txaio.start_logging(level='info') isSecure, host, port, resource, path, params = parse_url(self.url) # factory for use ApplicationSession def create(): cfg = ComponentConfig(self.realm, self.extra) try: session = make(cfg) except Exception as e: if start_reactor: # the app component could not be created .. fatal self.log.error(str(e)) reactor.stop() else: # if we didn't start the reactor, it's up to the # caller to deal with errors raise else: return session # create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory( create, url=self.url, serializers=self.serializers, proxy=self.proxy) # supress pointless log noise like # "Starting factory <autobahn.twisted.websocket.WampWebSocketClientFactory object at 0x2b737b480e10>"" transport_factory.noisy = False # if user passed ssl= but isn't using isSecure, we'll never # use the ssl argument which makes no sense. context_factory = None if self.ssl is not None: if not isSecure: raise RuntimeError( 'ssl= argument value passed to %s conflicts with the "ws:" ' 'prefix of the url argument. Did you mean to use "wss:"?' % self.__class__.__name__) context_factory = self.ssl elif isSecure: from twisted.internet.ssl import optionsForClientTLS context_factory = optionsForClientTLS(host) from twisted.internet import reactor if self.proxy is not None: from twisted.internet.endpoints import TCP4ClientEndpoint client = TCP4ClientEndpoint(reactor, self.proxy['host'], self.proxy['port']) transport_factory.contextFactory = context_factory elif isSecure: from twisted.internet.endpoints import SSL4ClientEndpoint assert context_factory is not None client = SSL4ClientEndpoint(reactor, host, port, context_factory) else: from twisted.internet.endpoints import TCP4ClientEndpoint client = TCP4ClientEndpoint(reactor, host, port) d = client.connect(transport_factory) # as the reactor shuts down, we wish to wait until we've sent # out our "Goodbye" message; leave() returns a Deferred that # fires when the transport gets to STATE_CLOSED def cleanup(proto): if hasattr(proto, '_session') and proto._session is not None: if proto._session.is_attached(): return proto._session.leave() elif proto._session.is_connected(): return proto._session.disconnect() # when our proto was created and connected, make sure it's cleaned # up properly later on when the reactor shuts down for whatever reason def init_proto(proto): reactor.addSystemEventTrigger('before', 'shutdown', cleanup, proto) return proto # if we connect successfully, the arg is a WampWebSocketClientProtocol d.addCallback(init_proto) # if the user didn't ask us to start the reactor, then they # get to deal with any connect errors themselves. if start_reactor: # if an error happens in the connect(), we save the underlying # exception so that after the event-loop exits we can re-raise # it to the caller. class ErrorCollector(object): exception = None def __call__(self, failure): self.exception = failure.value reactor.stop() connect_error = ErrorCollector() d.addErrback(connect_error) # now enter the Twisted reactor loop reactor.run() # if we exited due to a connection error, raise that to the # caller if connect_error.exception: raise connect_error.exception else: # let the caller handle any errors return d
def run(self, make, start_reactor=True): """ Run the application component. :param make: A factory that produces instances of :class:`autobahn.asyncio.wamp.ApplicationSession` when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`. :type make: callable :param start_reactor: if True (the default) this method starts the Twisted reactor and doesn't return until the reactor stops. If there are any problems starting the reactor or connect()-ing, we stop the reactor and raise the exception back to the caller. :returns: None is returned, unless you specify ``start_reactor=False`` in which case the Deferred that connect() returns is returned; this will callback() with an IProtocol instance, which will actually be an instance of :class:`WampWebSocketClientProtocol` """ if start_reactor: # only select framework, set loop and start logging when we are asked # start the reactor - otherwise we are running in a program that likely # already tool care of all this. from twisted.internet import reactor txaio.use_twisted() txaio.config.loop = reactor txaio.start_logging(level='info') isSecure, host, port, resource, path, params = parse_url(self.url) # factory for use ApplicationSession def create(): cfg = ComponentConfig(self.realm, self.extra) try: session = make(cfg) except Exception as e: if start_reactor: # the app component could not be created .. fatal self.log.error(str(e)) reactor.stop() else: # if we didn't start the reactor, it's up to the # caller to deal with errors raise else: return session # create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory(create, url=self.url, serializers=self.serializers, proxy=self.proxy) # supress pointless log noise like # "Starting factory <autobahn.twisted.websocket.WampWebSocketClientFactory object at 0x2b737b480e10>"" transport_factory.noisy = False # if user passed ssl= but isn't using isSecure, we'll never # use the ssl argument which makes no sense. context_factory = None if self.ssl is not None: if not isSecure: raise RuntimeError( 'ssl= argument value passed to %s conflicts with the "ws:" ' 'prefix of the url argument. Did you mean to use "wss:"?' % self.__class__.__name__) context_factory = self.ssl elif isSecure: from twisted.internet.ssl import optionsForClientTLS context_factory = optionsForClientTLS(host) from twisted.internet import reactor if self.proxy is not None: from twisted.internet.endpoints import TCP4ClientEndpoint client = TCP4ClientEndpoint(reactor, self.proxy['host'], self.proxy['port']) transport_factory.contextFactory = context_factory elif isSecure: from twisted.internet.endpoints import SSL4ClientEndpoint assert context_factory is not None client = SSL4ClientEndpoint(reactor, host, port, context_factory) else: from twisted.internet.endpoints import TCP4ClientEndpoint client = TCP4ClientEndpoint(reactor, host, port) d = client.connect(transport_factory) # as the reactor shuts down, we wish to wait until we've sent # out our "Goodbye" message; leave() returns a Deferred that # fires when the transport gets to STATE_CLOSED def cleanup(proto): if hasattr(proto, '_session') and proto._session is not None: if proto._session.is_attached(): return proto._session.leave() elif proto._session.is_connected(): return proto._session.disconnect() # when our proto was created and connected, make sure it's cleaned # up properly later on when the reactor shuts down for whatever reason def init_proto(proto): reactor.addSystemEventTrigger('before', 'shutdown', cleanup, proto) return proto # if we connect successfully, the arg is a WampWebSocketClientProtocol d.addCallback(init_proto) # if the user didn't ask us to start the reactor, then they # get to deal with any connect errors themselves. if start_reactor: # if an error happens in the connect(), we save the underlying # exception so that after the event-loop exits we can re-raise # it to the caller. class ErrorCollector(object): exception = None def __call__(self, failure): self.exception = failure.value reactor.stop() connect_error = ErrorCollector() d.addErrback(connect_error) # now enter the Twisted reactor loop reactor.run() # if we exited due to a connection error, raise that to the # caller if connect_error.exception: raise connect_error.exception else: # let the caller handle any errors return d
def test_parse_url04(self): self.assertEqual(parse_url("wss://localhost:443"), (True, 'localhost', 443, '/', '/', {}))
def test_parse_url06(self): self.assertEqual(parse_url("wss://localhost/ws?foo=bar"), (True, 'localhost', 443, '/ws?foo=bar', '/ws', { 'foo': ['bar'] }))
super().__init__(*args, **kwargs) self.emitter = emitter def buildProtocol(self, addr): conn = self.protocol(self, self.emitter) self.emitter.ws_conn = conn return conn if __name__ == '__main__': from ws_message_emitter import WSMessageEmitter log.startLogging(sys.stdout) ws_url = sys.argv[1] session_id, type_code = sys.argv[2:4] record_code = sys.argv[4] try: peg_proportion = float(sys.argv[5]) except ValueError: peg_proportion = None emitter = WSMessageEmitter(session_id, type_code, filter_value=record_code, **get_ws_confs(type_code), peg_proportion=peg_proportion) emitter.read_from_db(**db_creds) factory = ExogenousEventClientFactory(emitter, ws_url) url_parsed = parse_url(ws_url) host, port = url_parsed[1], url_parsed[2] reactor.connectTCP(host, port, factory) reactor.run()
def test_parse_url08(self): self.assertEqual(parse_url("wss://localhost/ws?foo=bar&moo=23&moo=44"), (True, 'localhost', 443, '/ws?foo=bar&moo=23&moo=44', '/ws', {'moo': ['23', '44'], 'foo': ['bar']}))
parser.add_argument('--silence', dest='silence', action="store_true", default=False, help='Silence log output.') parser.add_argument('--debug', dest='debug', action="store_true", default=False, help='Enable WebSocket debug output.') parser.add_argument('--interval', dest='interval', type=int, default=5, help='Worker stats update interval.') parser.add_argument('--profile', dest='profile', action="store_true", default=False, help='Enable profiling.') parser.add_argument('--fd', dest='fd', type=int, default=None, help='If given, this is a worker which will use provided FD and all other options are ignored.') parser.add_argument('--cpuid', dest='cpuid', type=int, default=None, help='If given, this is a worker which will use provided CPU core to set its affinity.') options = parser.parse_args() if options.profile and not hasStatprof: raise Exception("profiling requested, but statprof not installed") # parse WS URI into components and forward via options # FIXME: add TLS support isSecure, host, wsport, resource, path, params = parse_url(options.wsuri) options.wsport = wsport # if not options.silence: # log.startLogging(sys.stdout) if options.fd is not None: # run worker worker(options) else: if not options.silence: for m in startupMsgs: print m # run master master(options)
def test_parse_url05(self): self.assertEqual(parse_url("wss://localhost/ws"), (True, "localhost", 443, "/ws", "/ws", {}))
'--cpuid', dest='cpuid', type=int, default=None, help= 'If given, this is a worker which will use provided CPU core to set its affinity.' ) options = parser.parse_args() if options.profile and not hasStatprof: raise Exception("profiling requested, but statprof not installed") # parse WS URI into components and forward via options # FIXME: add TLS support isSecure, host, wsport, resource, path, params = parse_url(options.wsuri) options.wsport = wsport # if not options.silence: # log.startLogging(sys.stdout) if options.fd is not None: # run worker worker(options) else: if not options.silence: for m in startupMsgs: print m # run master master(options)
def test_parse_url06(self): self.assertEqual( parse_url("wss://localhost/ws?foo=bar"), (True, "localhost", 443, "/ws?foo=bar", "/ws", {"foo": ["bar"]}) )
return PerMessageDeflateOfferAccept(offer) self.setProtocolOptions(perMessageCompressionAccept=accept) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Autobahn Testee Server (Twisted)') parser.add_argument('--url', dest='url', type=str, default=u'ws://127.0.0.1:9001', help='The WebSocket fuzzing server URL.') parser.add_argument('--loglevel', dest='loglevel', type=str, default=u'info', help='Log level, eg "info" or "debug".') options = parser.parse_args() txaio.start_logging(level=options.loglevel) factory = TesteeServerFactory(options.url) _, _, port, _, _, _ = parse_url(options.url) loop = asyncio.get_event_loop() coro = loop.create_server(factory, port=port) server = loop.run_until_complete(coro) try: loop.run_forever() except KeyboardInterrupt: pass finally: server.close() loop.close()
def test_parse_url08(self): self.assertEqual( parse_url("wss://localhost/ws?foo=bar&moo=23&moo=44"), (True, "localhost", 443, "/ws?foo=bar&moo=23&moo=44", "/ws", {"moo": ["23", "44"], "foo": ["bar"]}), )
def compute_mgmt_uplink_config(log, cbdir, config, fn_reboot=None, use_activation_file=True, use_default_fabric=False): """ Determine the transport configuration of the management uplink for this node in the following order (using the first one that succeeds): * node activation file * management URL environment variable * node configuration file * built-in default ("master.xbr.network") :param cbdir: :param config: :param fn_reboot: :param use_activation_file: :param use_default_fabric: :return: """ fabric_transport_config = None # [1] pick up auto-activation files dropped by a master node (`${CBDIR}/key.activate`) if not fabric_transport_config and use_activation_file: def do_check_for_activation_file(activation_file, reboot_on_discover): if os.path.isfile(activation_file): tags = _parse_activation_file(activation_file) is_secure, hostname, port, _, _, _ = parse_url(tags['management-url']) config = { 'type': 'websocket', 'url': tags['management-url'], 'endpoint': { 'type': 'tcp', 'host': hostname, 'port': port, 'timeout': 5 } } if is_secure: config['endpoint']['tls'] = { 'hostname': hostname } _msg = 'Found auto-activation file "{}", using management URL "{}" - [1]'.format(activation_file, config['url']) log.info(click.style(_msg, fg='red', bold=True)) if reboot_on_discover and fn_reboot: # stop the node and enforce complete reboot - which will then pick up the new configuration fn_reboot() # return the management uplink transport configuration, as derived from the activation file return config # an activation file must be placed next to the node key pair (key.pub, key.priv) activation_file = os.path.join(cbdir, 'key.activate') # check and maybe load config from activation file fabric_transport_config = do_check_for_activation_file(activation_file, reboot_on_discover=False) # if there wasn't an activation file, periodically check for .. if not fabric_transport_config: lc = LoopingCall(do_check_for_activation_file, activation_file, reboot_on_discover=True) lc.start(1) log.info('Looping call to check for node activation file started! - [1]') # [2] management uplink configured via env var if not fabric_transport_config: url = os.environ['CROSSBAR_FABRIC_URL'].strip() if 'CROSSBAR_FABRIC_URL' in os.environ else '' if url != '': secure, host, tcp_port, _, _, _ = parse_url(url) fabric_transport_config = { 'type': 'websocket', 'url': url, 'endpoint': { 'type': 'tcp', 'host': host, 'port': tcp_port, 'timeout': 5 } } if secure: fabric_transport_config['endpoint']['tls'] = { 'hostname': host } log.info( click.style('Using management uplink at "{}" (from envvar) - [2]'.format(url), fg='red', bold=True)) # [3] user has configured a custom management uplink in the node configuration if not fabric_transport_config: if 'controller' in config and 'fabric' in config['controller'] and config['controller']['fabric']: fabric_config = config['controller']['fabric'] # allow to deactivate the management uplink connecting transport by setting "transport" to null fabric_transport_config = fabric_config.get('transport', None) if fabric_transport_config: url = fabric_transport_config.get('url', None) log.info( click.style('Using management uplink at "{}" (from node configuration) - [3]'.format(url), fg='red', bold=True)) else: log.info( click.style('Management uplink deactivated - [3]', fg='red', bold=True)) # [4] use hard-coded default management uplink if not fabric_transport_config and use_default_fabric: # default CFC (= our cloud hosted CFC service) fabric_transport_config = { 'type': 'websocket', 'url': 'wss://master.xbr.network/ws', 'endpoint': { 'type': 'tcp', 'host': 'master.xbr.network', 'port': 443, 'timeout': 5, 'tls': { 'hostname': 'master.xbr.network' } } } log.info( click.style( 'Using default fabric controller at URL "{}" (from envvar) - [4]'.format(fabric_transport_config['url']), fg='red', bold=True)) return fabric_transport_config
def test_parse_url02(self): self.assertEqual(parse_url("ws://localhost:80"), (False, "localhost", 80, "/", "/", {}))
def test_parse_url04(self): self.assertEqual(parse_url("wss://localhost:443"), (True, "localhost", 443, "/", "/", {}))
type=str, default=u'ws://127.0.0.1:9001', help='The WebSocket fuzzing server URL.') parser.add_argument('--loglevel', dest='loglevel', type=str, default=u'info', help='Log level, eg "info" or "debug".') options = parser.parse_args() txaio.start_logging(level=options.loglevel) factory = TesteeClientFactory(options.url, autobahn.asyncio.__ident__) _, host, port, _, _, _ = parse_url(options.url) loop = asyncio.get_event_loop() factory.resource = u'/getCaseCount' factory.endCaseId = None factory.currentCaseId = 0 factory.updateReports = True while True: factory._done = txaio.create_future() coro = loop.create_connection(factory, host, port) loop.run_until_complete(coro) loop.run_until_complete(factory._done)
def test_parse_url06(self): self.assertEqual(parse_url("wss://localhost/ws?foo=bar"), (True, 'localhost', 443, '/ws?foo=bar', '/ws', {'foo': ['bar']}))
def run(self, make): """ Run the application component. :param make: A factory that produces instances of :class:`autobahn.asyncio.wamp.ApplicationSession` when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`. :type make: callable """ # 1) factory for use ApplicationSession def create(): cfg = ComponentConfig(self.realm, self.extra) try: session = make(cfg) except Exception: self.log.failure("App session could not be created! ") asyncio.get_event_loop().stop() else: return session isSecure, host, port, resource, path, params = parse_url(self.url) if self.ssl is None: ssl = isSecure else: if self.ssl and not isSecure: raise RuntimeError( 'ssl argument value passed to %s conflicts with the "ws:" ' 'prefix of the url argument. Did you mean to use "wss:"?' % self.__class__.__name__) ssl = self.ssl # 2) create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory(create, url=self.url, serializers=self.serializers) # 3) start the client loop = asyncio.get_event_loop() txaio.use_asyncio() txaio.config.loop = loop coro = loop.create_connection(transport_factory, host, port, ssl=ssl) (transport, protocol) = loop.run_until_complete(coro) # start logging txaio.start_logging(level='info') try: loop.add_signal_handler(signal.SIGTERM, loop.stop) except NotImplementedError: # signals are not available on Windows pass # 4) now enter the asyncio event loop try: loop.run_forever() except KeyboardInterrupt: # wait until we send Goodbye if user hit ctrl-c # (done outside this except so SIGTERM gets the same handling) pass # give Goodbye message a chance to go through, if we still # have an active session if protocol._session: loop.run_until_complete(protocol._session.leave()) loop.close()