Ejemplo n.º 1
0
    def run(self):
        try:
            asyncio.set_event_loop(self._loop)

            txaio.use_asyncio()
            txaio.config.loop = self._loop

            # Info is too verbose, use error by default
            # TODO: Make logging level for autobahn configurable
            txaio.start_logging(level='error')

            # create a WAMP-over-WebSocket transport client factory
            transport_factory = WampWebSocketClientFactory(
                lambda: self._akcomponent_factory(self._decoupler, self.
                                                  _callback_executor, self.
                                                  _allow_exception),
                url=self._url)

            # Basic settings with most features disabled
            transport_factory.setProtocolOptions(failByDrop=False,
                                                 openHandshakeTimeout=5.,
                                                 closeHandshakeTimeout=1.)

            isSecure, host, port, _, _, _ = parse_ws_url(self._url)
            transport, protocol = self._loop.run_until_complete(
                self._loop.create_connection(transport_factory,
                                             host,
                                             port,
                                             ssl=isSecure))

            try:
                self._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:
                self._loop.run_until_complete(protocol._session.leave())

            self._loop.close()
        except Exception as e:
            errorStr = pformat(e)
            stderr.write(errorStr + "\n")

            # Wake the caller, this thread will terminate right after so the
            # error can be detected by checking if the thread is alive
            self._decoupler.set_joined()

        self._decoupler.unblock_caller()
Ejemplo n.º 2
0
    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
        """
        def _create_app_session():
            cfg = ComponentConfig(self._realm, self._extra)
            try:
                session = make(cfg)
            except Exception as e:
                # the app component could not be created .. fatal
                asyncio.get_event_loop().stop()
                raise e
            else:
                session.debug_app = self._debug_app
                return session

        self._transport_factory = WampWebSocketClientFactory(
            _create_app_session, url=self._url, serializers=self._serializers)

        if self._auto_ping_interval is not None and self._auto_ping_timeout is not None:
            self._transport_factory.setProtocolOptions(
                openHandshakeTimeout=self._open_handshake_timeout,
                autoPingInterval=self._auto_ping_interval,
                autoPingTimeout=self._auto_ping_timeout)

        txaio.use_asyncio()
        txaio.config.loop = self._loop

        asyncio. async (self._connect(), loop=self._loop)

        try:
            self._loop.add_signal_handler(signal.SIGTERM, self.stop)
        except NotImplementedError:
            # Ignore if not implemented. Means this program is running in windows.
            pass

        try:
            self._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

        self._closing = True

        if self._active_protocol and self._active_protocol._session:
            self._loop.run_until_complete(
                self._active_protocol._session.leave())
        self._loop.close()
Ejemplo n.º 3
0
 def _transport_factory(self):
     "create and return the transport factory"
     try:
         factory = WampWebSocketClientFactory(
             self._component,
             url=self.wmp_url,
             serializers=self.wmp_serializers,
             loop=self._loop)
     except Exception as exc:
         raise Exception("could not build transport factory: %s" % exc)
     else:
         factory._session_joined = asyncio.Future(loop=self._loop)
         self._logger.info("WAMP connecting to %s, realm '%s'" %
                           (self.wmp_url, self.wmp_realm))
         return factory
Ejemplo n.º 4
0
        async def run_client(loop):
            def fact():
                self.session = ClientSession(realm='realm1',
                                             user=user,
                                             token=token,
                                             task_cb=self.task_callback)
                return self.session

            transport_factory = WampWebSocketClientFactory(fact, url=wamp_url)

            parsed_url = urlparse(wamp_url)
            ssl = False
            if parsed_url.scheme == 'wss':
                #TODO: This is not good solution, rather consider either providing additional
                #CA certs - or for internal communication using plain TCP socket
                ssl = ssllib._create_unverified_context()

            hp = parsed_url.netloc.split(':')
            if len(hp) == 1:
                host, port = hp[0], 8080
            elif len(hp) == 2:
                host, port = hp[0], int(hp[1])
            else:
                raise ValueError('Invalid URL %s' % wamp_url)
            conn = loop.create_connection(transport_factory,
                                          host,
                                          port,
                                          ssl=ssl)
            (transport, protocol) = await conn
            self.protocol = protocol
            self.transport = transport
            self._ready.set()
Ejemplo n.º 5
0
   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:
            ## 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 = parseWsUrl(self.url)

      ## 2) create a WAMP-over-WebSocket transport client factory
      transport_factory = WampWebSocketClientFactory(create, url = self.url,
         debug = self.debug, debug_wamp = self.debug_wamp)

      ## 3) start the client
      loop = asyncio.get_event_loop()
      coro = loop.create_connection(transport_factory, host, port, ssl = isSecure)
      loop.run_until_complete(coro)

      ## 4) now enter the asyncio event loop
      loop.run_forever()
      loop.close()
Ejemplo n.º 6
0
    def register(self):
        def create():
            cfg = ComponentConfig(
                    self._realm,
                    extra={"dts": self._dts, "loop": self._loop, "log": self._log}
                    )

            # Set the asyncio loop for txaio
            txaio.config.loop = self._loop
            self._app = WampDtsMockApplication(cfg)
            self._app.debug_app = True

            return self._app

        isSecure, host, port, resource, path, params = parseWsUrl(self._url)
        transport_factory = WampWebSocketClientFactory(
                create, url=self._url,
                serializers=None, debug=True, debug_wamp=True,
                )

        while True:
            try:
                create_conn = self._loop.create_connection(transport_factory, host, port)
                (transport, protocol) = yield from create_conn
                self._log.debug("Connected to crossbar.io")
                break
            except ConnectionRefusedError:
                self._log.error("ConnectionRefusedError: sleeping for a second and retrying")
                yield from asyncio.sleep(1, self._loop)
Ejemplo n.º 7
0
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]
Ejemplo n.º 8
0
def _create_transport_factory(loop, transport, session_factory):
    """
    Create a WAMP-over-XXX transport factory.
    """
    if transport.type == u'websocket':
        serializers = _create_transport_serializers(transport)
        factory = WampWebSocketClientFactory(session_factory,
                                             url=transport.url,
                                             serializers=serializers)

    elif transport.type == u'rawsocket':
        serializer = _create_transport_serializer(transport.serializers[0])
        factory = WampRawSocketClientFactory(session_factory,
                                             serializer=serializer)

    else:
        assert (False), 'should not arrive here'

    # set the options one at a time so we can give user better feedback
    for k, v in transport.options.items():
        try:
            factory.setProtocolOptions(**{k: v})
        except (TypeError, KeyError):
            # this allows us to document options as snake_case
            # until everything internally is upgraded from
            # camelCase
            try:
                factory.setProtocolOptions(
                    **{_camel_case_from_snake_case(k): v})
            except (TypeError, KeyError):
                raise ValueError("Unknown {} transport option: {}={}".format(
                    transport.type, k, v))
    return factory
Ejemplo n.º 9
0
    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:
                # 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 = parseWsUrl(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,
                                                       debug=self.debug, debug_wamp=self.debug_wamp)

        # 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)
        loop.add_signal_handler(signal.SIGTERM, loop.stop)

        # 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
        loop.run_until_complete(protocol._session.leave())
        loop.close()
def _create_transport_factory(loop, transport, session_factory):
    """
    Create a WAMP-over-XXX transport factory.
    """
    if transport.type == u'websocket':
        serializers = _create_transport_serializers(transport)
        factory = WampWebSocketClientFactory(session_factory, url=transport.url, serializers=serializers)

    elif transport.type == u'rawsocket':
        serializer = _create_transport_serializer(transport.serializers[0])
        factory = WampRawSocketClientFactory(session_factory, serializer=serializer)

    else:
        assert(False), 'should not arrive here'

    # set the options one at a time so we can give user better feedback
    for k, v in transport.options.items():
        try:
            factory.setProtocolOptions(**{k: v})
        except (TypeError, KeyError):
            # this allows us to document options as snake_case
            # until everything internally is upgraded from
            # camelCase
            try:
                factory.setProtocolOptions(
                    **{_camel_case_from_snake_case(k): v}
                )
            except (TypeError, KeyError):
                raise ValueError(
                    "Unknown {} transport option: {}={}".format(transport.type, k, v)
                )
    return factory
Ejemplo n.º 11
0
    def run(self, make, app_cfg=None):
        """
        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
        """

        self.loop = self.loop or asyncio.get_event_loop()
        txaio.use_asyncio()
        txaio.config.loop = self.loop

        # 1) factory for use ApplicationSession
        def create():
            cfg = ComponentConfig(self.realm, self.extra)
            session = make(cfg, app_cfg=app_cfg, loop=self.loop)
            session.debug_app = self.debug_app
            return session

        isSecure, host, port, resource, path, params = parseWsUrl(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,
            debug=self.debug,
            debug_wamp=self.debug_wamp,
            loop=self.loop)

        # 3) start the client
        coro = self.loop.create_connection(transport_factory,
                                           host,
                                           port,
                                           ssl=ssl)
        (self.transport, self.protocol) = self.loop.run_until_complete(coro)
Ejemplo n.º 12
0
    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
        """

        def _create_app_session():
            cfg = ComponentConfig(self._realm, self._extra)
            try:
                session = make(cfg)
            except Exception as e:
                # the app component could not be created .. fatal
                asyncio.get_event_loop().stop()
                raise e
            else:
                session.debug_app = self._debug_app
                return session

        self._transport_factory = WampWebSocketClientFactory(_create_app_session, url=self._url, serializers=self._serializers)

        if self._auto_ping_interval is not None and self._auto_ping_timeout is not None:
            self._transport_factory.setProtocolOptions(autoPingInterval=self._auto_ping_interval, autoPingTimeout=self._auto_ping_timeout)

        txaio.use_asyncio()
        txaio.config.loop = self._loop

        asyncio.async(self._connect(), loop=self._loop)
        self._loop.add_signal_handler(signal.SIGTERM, self.stop)

        try:
            self._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

        self._closing = True

        if self._active_protocol and self._active_protocol._session:
            self._loop.run_until_complete(self._active_protocol._session.leave())
        self._loop.close()
Ejemplo n.º 13
0
    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
Ejemplo n.º 14
0
    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())
Ejemplo n.º 15
0
    import importlib
    c = args.component.split('.')
    mod, klass = '.'.join(c[:-1]), c[-1]
    app = importlib.import_module(mod)

    ## .. and set the session class on the factory
    ##
    session_factory.session = getattr(app, klass)

    if args.transport == "websocket":

        ## create a WAMP-over-WebSocket transport client factory
        ##
        from autobahn.asyncio.websocket import WampWebSocketClientFactory
        transport_factory = WampWebSocketClientFactory(session_factory,
                                                       url=args.url,
                                                       debug_wamp=args.debug)
        transport_factory.setProtocolOptions(failByDrop=False)

    elif args.transport in ['rawsocket-json', 'rawsocket-msgpack']:

        ## create a WAMP-over-RawSocket transport client factory
        ##
        if args.transport == 'rawsocket-msgpack':
            from autobahn.wamp.serializer import MsgPackSerializer
            serializer = MsgPackSerializer()
        elif args.transport == 'rawsocket-json':
            from autobahn.wamp.serializer import JsonSerializer
            serializer = JsonSerializer()
        else:
            raise Exception("should not arrive here")
Ejemplo n.º 16
0
class ApplicationRunner(object):
    """
    This class is a slightly modified version of autobahn.asyncio.wamp.ApplicationRunner
    with auto reconnection feature to with customizable strategies.
    """

    def __init__(self, url, realm, extra=None, serializers=None, debug_app=False,
                 ssl=None, loop=None, retry_strategy=BackoffStrategy(), auto_ping_interval=10, auto_ping_timeout=27):
        """
        :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_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
        :param auto_ping_interval: How often to send a keep-alive ping to the router (in seconds).
           A value of None turns off pings.
        :type auto_ping_interval: int
        :param auto_ping_timeout: Consider the connection dropped if the router does not respond to our
           ping for more than X seconds.
        :type auto_ping_timeout: int
        """
        self._url = url
        self._realm = realm
        self._extra = extra or dict()
        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._auto_ping_interval = auto_ping_interval
        self._auto_ping_timeout = auto_ping_timeout

        self._isSecure, self._host, self._port, _, _, _ = parseWsUrl(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 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
        """

        def _create_app_session():
            cfg = ComponentConfig(self._realm, self._extra)
            try:
                session = make(cfg)
            except Exception as e:
                # the app component could not be created .. fatal
                asyncio.get_event_loop().stop()
                raise e
            else:
                session.debug_app = self._debug_app
                return session

        self._transport_factory = WampWebSocketClientFactory(_create_app_session, url=self._url, serializers=self._serializers)

        if self._auto_ping_interval is not None and self._auto_ping_timeout is not None:
            self._transport_factory.setProtocolOptions(autoPingInterval=self._auto_ping_interval, autoPingTimeout=self._auto_ping_timeout)

        txaio.use_asyncio()
        txaio.config.loop = self._loop

        asyncio.async(self._connect(), loop=self._loop)
        self._loop.add_signal_handler(signal.SIGTERM, self.stop)

        try:
            self._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

        self._closing = True

        if self._active_protocol and self._active_protocol._session:
            self._loop.run_until_complete(self._active_protocol._session.leave())
        self._loop.close()

    @asyncio.coroutine
    def _connect(self):
        self._active_protocol = None
        self._retry_strategy.reset_retry_interval()
        while True:
            try:
                _, protocol = yield from self._loop.create_connection(self._transport_factory, self._host, self._port, ssl=self._ssl)
                protocol.is_closed.add_done_callback(self._reconnect)
                self._active_protocol = protocol
                return
            except OSError:
                print('Connection failed')
                if self._retry_strategy.retry():
                    retry_interval = self._retry_strategy.get_retry_interval()
                    print('Retry in {} seconds'.format(retry_interval))
                    yield from asyncio.sleep(retry_interval)
                else:
                    print('Exceeded retry count')
                    self._loop.stop()
                    raise ExceededRetryCount()

                self._retry_strategy.increase_retry_interval()

    def _reconnect(self, f):
        # Reconnect
        print('Connection lost')
        if not self._closing:
            print('Reconnecting')
            asyncio.async(self._connect(), loop=self._loop)

    def stop(self, *args):
        self._loop.stop()
Ejemplo n.º 17
0
        async def do_connect() -> None:
            def create_session() -> AsphaltSession:
                session = AsphaltSession(self.realm, self.auth_method, self.auth_id,
                                         self.auth_secret)
                session.on('disconnect', on_disconnect)
                session.on('join', on_join)
                session.on('leave', on_leave)
                return session

            def on_disconnect(session: AsphaltSession, was_clean: bool):
                if not was_clean:
                    join_future.set_exception(ConnectionError('connection closed unexpectedly'))

            def on_join(session: AsphaltSession, details: SessionDetails):
                session.off('disconnect')
                join_future.set_result((session, details))

            def on_leave(session: AsphaltSession, details):
                self._session = None
                self._session_details = None
                self._connect_task = None
                self._subscriptions.clear()
                self._registrations.clear()
                self.realm_left.dispatch(details)
                if not session._goodbye_sent:
                    if not join_future.done():
                        join_future.set_exception(ConnectionError(details.message))
                    elif join_future.done():
                        logger.error('Connection lost; reconnecting')
                        self.connect()

            proto = 'wss' if self.tls else 'ws'
            url = '{proto}://{self.host}:{self.port}{self.path}'.format(proto=proto, self=self)
            logger.info('Connecting to %s', url)
            serializers = [wrap_serializer(self.serializer)]
            attempts = 0

            while self._session is None:
                transport = None
                attempts += 1
                try:
                    join_future = self._loop.create_future()
                    transport_factory = WampWebSocketClientFactory(
                        create_session, url=url, serializers=serializers, loop=self._loop)
                    transport_factory.setProtocolOptions(**self.protocol_options)
                    with timeout(self.connection_timeout):
                        transport, protocol = await self._loop.create_connection(
                            transport_factory, self.host, self.port,
                            ssl=(cast(Optional[SSLContext], self.tls_context) or
                                 True if self.tls else False))

                        # Connection established; wait for the session to join the realm
                        logger.info('Connected to %s; attempting to join realm %s', self.host,
                                    self.realm)
                        self._session, self._session_details = await join_future

                    # Register exception mappings with the session
                    logger.info(
                        'Realm %r joined; registering %d procedure(s), %d subscription(s) and %d '
                        'exception(s)', self._session_details.realm,
                        len(self._registry.procedures), len(self._registry.subscriptions),
                        len(self._registry.exceptions))
                    for error, exc_type in self._registry.exceptions.items():
                        self._session.define(exc_type, error)

                    # Register procedures with the session
                    for procedure in self._registry.procedures.values():
                        with timeout(10):
                            await self._register(procedure)

                    # Register subscribers with the session
                    for subscriber in self._registry.subscriptions:
                        with timeout(10):
                            await self._subscribe(subscriber)
                except Exception as e:
                    if self._session:
                        await self.stop()
                    elif transport:
                        transport.close()

                    if isinstance(e, CancelledError):
                        logger.info('Connection attempt cancelled')
                        raise

                    if (self.max_reconnection_attempts is not None and
                            attempts > self.max_reconnection_attempts):
                        raise

                    logger.warning('Connection failed (attempt %d): %s(%s); reconnecting in %s '
                                   'seconds', attempts, e.__class__.__name__, e,
                                   self.reconnect_delay)
                    await sleep(self.reconnect_delay)

            # Notify listeners that we've joined the realm
            self.realm_joined.dispatch(self._session_details)
            logger.info('Joined realm %r', self.realm)
Ejemplo n.º 18
0
        async def do_connect() -> None:
            proto = 'wss' if self.ssl else 'ws'
            url = '{proto}://{self.host}:{self.port}{self.path}'.format(
                proto=proto, self=self)
            logger.debug('Connecting to %s:%d (ssl=%s)', self.host, self.port,
                         bool(self.ssl))
            serializers = [wrap_serializer(self.serializer)]
            loop = txaio.config.loop = get_event_loop()
            transport = None
            attempts = 0

            while self._session is None:
                attempts += 1
                try:
                    join_future = Future()
                    session_factory = partial(AsphaltSession, self.realm,
                                              self.auth_method, self.auth_id,
                                              self.auth_secret, join_future)
                    transport_factory = WampWebSocketClientFactory(
                        session_factory,
                        url=url,
                        serializers=serializers,
                        loop=loop)
                    transport, protocol = await loop.create_connection(
                        transport_factory, self.host, self.port, ssl=self.ssl)

                    # Connection established; wait for the session to join the realm
                    logger.debug('Connected; attempting to join realm %s',
                                 self.realm)
                    self._session_details, self._session = await wait_for(
                        join_future, timeout=5, loop=loop)

                    # Register exception mappings with the session
                    logger.debug(
                        'Realm joined; registering exceptions, subscriptions and procedures'
                    )
                    for error, exc_type in self.registry.exceptions.items():
                        self._session.define(exc_type, error)

                    # Register procedures and subscribers with the session
                    tasks = [
                        loop.create_task(self._subscribe(subscriber))
                        for subscriber in self.registry.subscriptions
                    ]
                    tasks += [
                        loop.create_task(self._register(procedure))
                        for procedure in self.registry.procedures.values()
                    ]
                    if tasks:
                        done, not_done = await wait(
                            tasks,
                            loop=loop,
                            timeout=10,
                            return_when=FIRST_EXCEPTION)
                        for task in done:
                            if task.exception():
                                raise task.exception()
                except CancelledError:
                    raise
                except Exception as e:
                    if transport:
                        transport.close()

                    self._session = self._session_details = transport = None
                    if (self.max_reconnection_attempts is not None
                            and attempts > self.max_reconnection_attempts):
                        raise

                    logger.info(
                        'Connection failed (attempt %d): %s(%s); reconnecting in %d '
                        'seconds', attempts, e.__class__.__name__, e,
                        self.reconnect_delay)
                    await sleep(self.reconnect_delay)

            self._session.on('leave', _leave_callback)

            # Notify listeners that we've joined the realm
            self.realm_joined.dispatch(self._session_details)

            logger.debug('Registration complete')
Ejemplo n.º 19
0
class ApplicationRunner(object):
    """
    This class is a slightly modified version of autobahn.asyncio.wamp.ApplicationRunner
    with auto reconnection feature to with customizable strategies.
    """
    def __init__(self,
                 url,
                 realm,
                 extra=None,
                 serializers=None,
                 debug_app=False,
                 ssl=None,
                 loop=None,
                 retry_strategy=BackoffStrategy(),
                 open_handshake_timeout=30,
                 auto_ping_interval=10,
                 auto_ping_timeout=27):
        """
        :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_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
        :param open_handshake_timeout: How long to wait for the opening handshake to complete (in seconds).
        :param auto_ping_interval: How often to send a keep-alive ping to the router (in seconds).
           A value of None turns off pings.
        :type auto_ping_interval: int
        :param auto_ping_timeout: Consider the connection dropped if the router does not respond to our
           ping for more than X seconds.
        :type auto_ping_timeout: int
        """
        self._url = url
        self._realm = realm
        self._extra = extra or dict()
        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._open_handshake_timeout = open_handshake_timeout
        self._auto_ping_interval = auto_ping_interval
        self._auto_ping_timeout = auto_ping_timeout

        self._isSecure, self._host, self._port, _, _, _ = parseWsUrl(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 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
        """
        def _create_app_session():
            cfg = ComponentConfig(self._realm, self._extra)
            try:
                session = make(cfg)
            except Exception as e:
                # the app component could not be created .. fatal
                asyncio.get_event_loop().stop()
                raise e
            else:
                session.debug_app = self._debug_app
                return session

        self._transport_factory = WampWebSocketClientFactory(
            _create_app_session, url=self._url, serializers=self._serializers)

        if self._auto_ping_interval is not None and self._auto_ping_timeout is not None:
            self._transport_factory.setProtocolOptions(
                openHandshakeTimeout=self._open_handshake_timeout,
                autoPingInterval=self._auto_ping_interval,
                autoPingTimeout=self._auto_ping_timeout)

        txaio.use_asyncio()
        txaio.config.loop = self._loop

        asyncio. async (self._connect(), loop=self._loop)

        try:
            self._loop.add_signal_handler(signal.SIGTERM, self.stop)
        except NotImplementedError:
            # Ignore if not implemented. Means this program is running in windows.
            pass

        try:
            self._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

        self._closing = True

        if self._active_protocol and self._active_protocol._session:
            self._loop.run_until_complete(
                self._active_protocol._session.leave())
        self._loop.close()

    @asyncio.coroutine
    def _connect(self):
        self._active_protocol = None
        self._retry_strategy.reset_retry_interval()
        while True:
            try:
                _, protocol = yield from self._loop.create_connection(
                    self._transport_factory,
                    self._host,
                    self._port,
                    ssl=self._ssl)
                protocol.is_closed.add_done_callback(self._reconnect)
                self._active_protocol = protocol
                return
            except OSError:
                print('Connection failed')
                if self._retry_strategy.retry():
                    retry_interval = self._retry_strategy.get_retry_interval()
                    print('Retry in {} seconds'.format(retry_interval))
                    yield from asyncio.sleep(retry_interval)
                else:
                    print('Exceeded retry count')
                    self._loop.stop()
                    raise ExceededRetryCount()

                self._retry_strategy.increase_retry_interval()

    def _reconnect(self, f):
        # Reconnect
        print('Connection lost')
        if not self._closing:
            print('Reconnecting')
            asyncio. async (self._connect(), loop=self._loop)

    def stop(self, *args):
        self._loop.stop()
Ejemplo n.º 20
0
    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()
Ejemplo n.º 21
0
    def run(self, make, start_loop=True, log_level='info'):
        """
        Run the application component. Under the hood, this runs the event
        loop (unless `start_loop=False` is passed) so won't return
        until the program is done.

        :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_loop: When ``True`` (the default) this method
            start a new asyncio loop.
        :type start_loop: bool

        :returns: None is returned, unless you specify
            `start_loop=False` in which case the coroutine from calling
            `loop.create_connection()` is returned. This will yield the
            (transport, protocol) pair.
        """
        if callable(make):

            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
        else:
            create = make

        if self.url.startswith(u'rs'):
            # try to parse RawSocket URL ..
            isSecure, host, port = parse_rs_url(self.url)

            # use the first configured serializer if any (which means, auto-choose "best")
            serializer = self.serializers[0] if self.serializers else None

            # create a WAMP-over-RawSocket transport client factory
            transport_factory = WampRawSocketClientFactory(
                create, serializer=serializer)

        else:
            # try to parse WebSocket URL ..
            isSecure, host, port, resource, path, params = parse_ws_url(
                self.url)

            # create a WAMP-over-WebSocket transport client factory
            transport_factory = WampWebSocketClientFactory(
                create,
                url=self.url,
                serializers=self.serializers,
                proxy=self.proxy,
                headers=self.headers)

            # client WebSocket settings - similar to:
            # - http://crossbar.io/docs/WebSocket-Compression/#production-settings
            # - http://crossbar.io/docs/WebSocket-Options/#production-settings

            # The permessage-deflate extensions offered to the server ..
            offers = [PerMessageDeflateOffer()]

            # Function to accept permessage_delate responses from the server ..
            def accept(response):
                if isinstance(response, PerMessageDeflateResponse):
                    return PerMessageDeflateResponseAccept(response)

            # set WebSocket options for all client connections
            transport_factory.setProtocolOptions(
                maxFramePayloadSize=1048576,
                maxMessagePayloadSize=1048576,
                autoFragmentSize=65536,
                failByDrop=False,
                openHandshakeTimeout=2.5,
                closeHandshakeTimeout=1.,
                tcpNoDelay=True,
                autoPingInterval=10.,
                autoPingTimeout=5.,
                autoPingSize=4,
                perMessageCompressionOffers=offers,
                perMessageCompressionAccept=accept)
        # SSL context for client connection
        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

        # start the client connection
        loop = asyncio.get_event_loop()
        txaio.use_asyncio()
        txaio.config.loop = loop
        coro = loop.create_connection(transport_factory, host, port, ssl=ssl)

        # start a asyncio loop
        if not start_loop:
            return coro
        else:
            (transport, protocol) = loop.run_until_complete(coro)

            # start logging
            txaio.start_logging(level=log_level)

            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()
Ejemplo n.º 22
0
   import importlib
   c = args.component.split('.')
   mod, klass = '.'.join(c[:-1]), c[-1]
   app = importlib.import_module(mod)

   ## .. and set the session class on the factory
   ##
   session_factory.session = getattr(app, klass)


   if args.transport == "websocket":

      ## create a WAMP-over-WebSocket transport client factory
      ##
      from autobahn.asyncio.websocket import WampWebSocketClientFactory
      transport_factory = WampWebSocketClientFactory(session_factory, url = args.url, debug_wamp = args.debug)
      transport_factory.setProtocolOptions(failByDrop = False)

   elif args.transport in ['rawsocket-json', 'rawsocket-msgpack']:

      ## create a WAMP-over-RawSocket transport client factory
      ##
      if args.transport == 'rawsocket-msgpack':
         from autobahn.wamp.serializer import MsgPackSerializer
         serializer = MsgPackSerializer()
      elif args.transport == 'rawsocket-json':
         from autobahn.wamp.serializer import JsonSerializer
         serializer = JsonSerializer()
      else:
         raise Exception("should not arrive here")