예제 #1
0
    def __init__(self, factory, config):

        ## transport configuration
        self._config = config

        ## WAMP serializer
        ##
        serid = config.get('serializer', 'msgpack')

        if serid == 'json':
            ## try JSON WAMP serializer
            try:
                from autobahn.wamp.serializer import JsonSerializer
                serializer = JsonSerializer()
            except ImportError:
                raise Exception("could not load WAMP-JSON serializer")

        elif serid == 'msgpack':
            ## try MsgPack WAMP serializer
            try:
                from autobahn.wamp.serializer import MsgPackSerializer
                serializer = MsgPackSerializer()
                serializer._serializer.ENABLE_V5 = False  ## FIXME
            except ImportError:
                raise Exception("could not load WAMP-MsgPack serializer")

        else:
            raise Exception("invalid WAMP serializer '{}'".format(serid))

        WampRawSocketClientFactory.__init__(self, factory, serializer)
예제 #2
0
    def __init__(self, factory, config):

        # transport configuration
        self._config = config

        # WAMP serializer
        #
        serid = config.get('serializer', 'msgpack')

        if serid == 'json':
            # try JSON WAMP serializer
            try:
                from autobahn.wamp.serializer import JsonSerializer
                serializer = JsonSerializer()
            except ImportError:
                raise Exception("could not load WAMP-JSON serializer")

        elif serid == 'msgpack':
            # try MsgPack WAMP serializer
            try:
                from autobahn.wamp.serializer import MsgPackSerializer
                serializer = MsgPackSerializer()
                serializer._serializer.ENABLE_V5 = False  # FIXME
            except ImportError:
                raise Exception("could not load WAMP-MsgPack serializer")

        else:
            raise Exception("invalid WAMP serializer '{}'".format(serid))

        WampRawSocketClientFactory.__init__(self, factory, serializer)
예제 #3
0
    def test_handshake_succeeds(self):
        """
        A client can connect to a server.
        """
        session_mock = Mock()
        t = FakeTransport()
        f = WampRawSocketClientFactory(lambda: session_mock)
        p = WampRawSocketClientProtocol()
        p.transport = t
        p.factory = f

        server_session_mock = Mock()
        st = FakeTransport()
        sf = WampRawSocketServerFactory(lambda: server_session_mock)
        sp = WampRawSocketServerProtocol()
        sp.transport = st
        sp.factory = sf

        sp.connectionMade()
        p.connectionMade()

        # Send the server the client handshake
        sp.dataReceived(t._written[0:1])
        sp.dataReceived(t._written[1:4])

        # Send the client the server handshake
        p.dataReceived(st._written)

        # The handshake succeeds, a session on each end is created
        # onOpen is called on the session
        session_mock.onOpen.assert_called_once_with(p)
        server_session_mock.onOpen.assert_called_once_with(sp)
예제 #4
0
def _create_transport_factory(reactor, 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
예제 #5
0
def _create_transport_factory(reactor, transport_config, session_factory):
    """
    Create a WAMP-over-XXX transport factory.
    """
    if transport_config['type'] == 'websocket':
        # FIXME: forward WebSocket options
        serializers = _create_transport_serializers(transport_config)
        return WampWebSocketClientFactory(session_factory, url=transport_config['url'], serializers=serializers)

    elif transport_config['type'] == 'rawsocket':
        # FIXME: forward RawSocket options
        serializer = _create_transport_serializer(transport_config.get('serializer', u'json'))
        return WampRawSocketClientFactory(session_factory, serializer=serializer)

    else:
        assert(False), 'should not arrive here'
예제 #6
0
def _create_transport_factory(reactor, transport, session_factory):
    """
    Create a WAMP-over-XXX transport factory.
    """
    if transport.type == 'websocket':
        # FIXME: forward WebSocket options
        serializers = _create_transport_serializers(transport)
        return WampWebSocketClientFactory(session_factory, url=transport.url, serializers=serializers)

    elif transport.type == 'rawsocket':
        # FIXME: forward RawSocket options
        serializer = _create_transport_serializer(transport.serializer)
        return WampRawSocketClientFactory(session_factory, serializer=serializer)

    else:
        assert(False), 'should not arrive here'
예제 #7
0
    def run(self, make, start_reactor=True, auto_reconnect=False, log_level='info'):
        """
        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=log_level)

        if callable(make):
            # factory for use ApplicationSession
            def create():
                cfg = ComponentConfig(self.realm, self.extra)
                try:
                    session = make(cfg)
                except Exception:
                    self.log.failure('ApplicationSession could not be instantiated: {log_failure.value}')
                    if start_reactor and reactor.running:
                        reactor.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)

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

        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)

        # supress pointless log noise
        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)

        # 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

        use_service = False
        if auto_reconnect:
            try:
                # since Twisted 16.1.0
                from twisted.application.internet import ClientService
                use_service = True
            except ImportError:
                use_service = False

        if use_service:
            self.log.debug('using t.a.i.ClientService')
            # this is automatically reconnecting
            service = ClientService(client, transport_factory)
            service.startService()
            d = service.whenConnected()
        else:
            # this is only connecting once!
            self.log.debug('using t.i.e.connect()')
            d = client.connect(transport_factory)

        # 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
예제 #8
0
        def test_minimal(self):

            embedded_components, client_components = [], [
                Case2_Backend, Case2_Frontend
            ]

            ## create a WAMP router factory
            ##
            router_factory = RouterFactory()

            ## create a WAMP router session factory
            ##
            session_factory = RouterSessionFactory(router_factory)

            ## .. and create and add an WAMP application session to
            ## run next to the router
            ##
            config = types.ComponentConfig(realm=self.realm,
                                           extra={'caselog': 'case1.log'})
            try:
                log = io.open('caselog.log', 'w')
            except Exception as e:
                print(e)
                return

    #      log = io.open(config.extra['caselog'], 'w')
            config.log = log
            config.dlog = []
            config.components = []

            config.all_done = []

            for C in embedded_components:
                one_done = Deferred()
                config.all_done.append(one_done)
                c = C(config, one_done)
                config.components.append(c)
                session_factory.add(c)

            if self.transport == "websocket":

                ## create a WAMP-over-WebSocket transport server factory
                ##
                transport_factory = WampWebSocketServerFactory(
                    session_factory, debug_wamp=self.debug)
                transport_factory.setProtocolOptions(failByDrop=False,
                                                     openHandshakeTimeout=0,
                                                     closeHandshakeTimeout=0)

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

                ## create a WAMP-over-RawSocket transport server factory
                ##
                if self.transport == 'rawsocket-msgpack':
                    serializer = MsgPackSerializer()
                elif self.transport == 'rawsocket-json':
                    serializer = JsonSerializer()
                else:
                    raise Exception("should not arrive here")

                transport_factory = WampRawSocketServerFactory(
                    session_factory, serializer, debug=self.debug)

            else:
                raise Exception("should not arrive here")

            ## start the server from an endpoint
            ##
            from twisted.internet import reactor
            server = serverFromString(reactor, self.server)
            d = server.listen(transport_factory)

            def onlisten(port):
                config.port = port

            d.addCallback(onlisten)

            clients = []
            clients_d = []
            for C in client_components:
                ## create a WAMP application session factory
                ##
                session_factory = ApplicationSessionFactory(config)

                one_done = Deferred()
                config.all_done.append(one_done)

                def make_make(Klass, done):
                    def make(config):
                        c = Klass(config, done)
                        config.components.append(c)
                        return c

                    return make

                ## .. and set the session class on the factory
                ##
                session_factory.session = make_make(C, one_done)

                if self.transport == "websocket":

                    serializers = [JsonSerializer()]

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

                    if True:

                        def maker(Klass):
                            class TestClientProtocol(
                                    WampWebSocketClientProtocol):
                                def onOpen(self):
                                    self.txcnt = 0
                                    self.rxcnt = 0
                                    WampWebSocketClientProtocol.onOpen(self)

                                def sendMessage(self, bytes, isBinary):
                                    self.txcnt += 1
                                    print("> : {:>3} : {:<20} : {}".format(
                                        self.txcnt, Klass.__name__, bytes))
                                    WampWebSocketClientProtocol.sendMessage(
                                        self, bytes, isBinary)

                                def onMessage(self, bytes, isBinary):
                                    self.rxcnt += 1
                                    print("< : {:>3} : {:<20} : {}".format(
                                        self.rxcnt, Klass.__name__, bytes))
                                    WampWebSocketClientProtocol.onMessage(
                                        self, bytes, isBinary)

                            return TestClientProtocol

                        transport_factory.protocol = maker(C)
                    else:
                        transport_factory.protocol = WampWebSocketClientProtocol

                    transport_factory.setProtocolOptions(
                        failByDrop=False,
                        openHandshakeTimeout=0,
                        closeHandshakeTimeout=0)

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

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

                    transport_factory = WampRawSocketClientFactory(
                        session_factory, serializer, debug=self.debug)

                ## start the client from an endpoint
                ##
                cl = clientFromString(reactor, self.client)
                clients_d.append(cl.connect(transport_factory))

                clients.append(cl)

            config.connected_clients = None

            def client_connected(res):
                config.connected_clients = [
                    proto for success, proto in res if success
                ]

            DeferredList(clients_d).addCallback(client_connected)

            d = DeferredList(config.all_done, consumeErrors=True)

            #d = config.components[1]._done

            def done(res):
                log.flush()
                log.close()
                if config.port:
                    config.port.stopListening()
                if config.connected_clients:
                    for proto in config.connected_clients:
                        proto.transport.abortConnection()
                print("Log length: {}".format(len(config.dlog)))
                print(config.dlog)
                #from twisted.internet import reactor
                #reactor.callLater(1, reactor.stop)

            def error(err):
                print(err)

            d.addCallbacks(done, error)

            #      d2 = Deferred()

            return d
예제 #9
0
    def run(self, make, start_reactor=True, auto_reconnect=False, log_level='info', endpoint=None, reactor=None):
        """
        Run the application component.

        :param make: A factory that produces instances of :class:`autobahn.twisted.wamp.ApplicationSession`
           when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`.
        :type make: callable

        :param start_reactor: When ``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`
        """
        self.log.debug('{klass}.run()', klass=self.__class__.__name__)

        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=log_level)

        if callable(make):
            # factory for use ApplicationSession
            def create():
                cfg = ComponentConfig(self.realm, self.extra, runner=self)
                try:
                    session = make(cfg)
                except Exception:
                    self.log.failure('ApplicationSession could not be instantiated: {log_failure.value}')
                    if start_reactor and reactor.running:
                        reactor.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)

        # supress pointless log noise
        transport_factory.noisy = False

        if endpoint:
            client = endpoint
        else:
            # 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)

        # 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):
            self._connect_successes += 1
            reactor.addSystemEventTrigger('before', 'shutdown', cleanup, proto)
            return proto

        use_service = False
        if auto_reconnect:
            try:
                # since Twisted 16.1.0
                from twisted.application.internet import ClientService
                from twisted.application.internet import backoffPolicy
                use_service = True
            except ImportError:
                use_service = False

        if use_service:
            # this code path is automatically reconnecting ..
            self.log.debug('using t.a.i.ClientService')

            if self.max_retries or self.initial_retry_delay or self.max_retry_delay or self.retry_delay_growth or self.retry_delay_jitter:
                kwargs = {}
                for key, val in [('initialDelay', self.initial_retry_delay),
                                 ('maxDelay', self.max_retry_delay),
                                 ('factor', self.retry_delay_growth),
                                 ('jitter', lambda: random.random() * self.retry_delay_jitter)]:
                    if val:
                        kwargs[key] = val

                # retry policy that will only try to reconnect if we connected
                # successfully at least once before (so it fails on host unreachable etc ..)
                def retry(failed_attempts):
                    if self._connect_successes > 0 and (self.max_retries == -1 or failed_attempts < self.max_retries):
                        return backoffPolicy(**kwargs)(failed_attempts)
                    else:
                        print('hit stop')
                        self.stop()
                        return 100000000000000
            else:
                retry = backoffPolicy()

            self._client_service = ClientService(client, transport_factory, retryPolicy=retry)
            self._client_service.startService()

            d = self._client_service.whenConnected()

        else:
            # this code path is only connecting once!
            self.log.debug('using t.i.e.connect()')

            d = client.connect(transport_factory)

        # 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
예제 #10
0
        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")

            from autobahn.twisted.rawsocket import WampRawSocketClientFactory
            transport_factory = WampRawSocketClientFactory(session_factory,
                                                           serializer,
                                                           debug=args.debug)

        ## start the client from an endpoint
        ##
        cl = clientFromString(reactor, args.client)
        clients_d.append(cl.connect(transport_factory))

        clients.append(cl)

    d = DeferredList(config.all_done)

    #d = config.components[1]._done


    def done(res):
예제 #11
0
   from autobahn.wamp.router import RouterFactory
   router_factory = RouterFactory()


   ## create a WAMP router session factory
   ##
   from autobahn.twisted.wamp import RouterSessionFactory
   session_factory = RouterSessionFactory(router_factory)
   session_factory.add(TestService())


   from autobahn.wamp.serializer import MsgPackSerializer
   serializer = MsgPackSerializer()

   from autobahn.twisted.rawsocket import WampRawSocketClientFactory, WampRawSocketClientProtocol
   transport_factory = WampRawSocketClientFactory(session_factory, serializer, debug = args.debug)

   class WorkerClientProtocol(WampRawSocketClientProtocol):

      def connectionMade(self):
         WampRawSocketClientProtocol.connectionMade(self)


      def connectionLost(self, reason):
         print "X"*10
         WampRawSocketClientProtocol.connectionLost(self, reason)
         reactor.stop()

   transport_factory.protocol = WorkerClientProtocol

예제 #12
0
    def run(self,
            make,
            start_reactor: bool = True,
            auto_reconnect: bool = False,
            log_level: str = 'info',
            endpoint: Optional[IStreamClientEndpoint] = None,
            reactor: Optional[IReactorCore] = None
            ) -> Union[type(None), Deferred]:
        """
        Run the application component.

        :param make: A factory that produces instances of :class:`autobahn.twisted.wamp.ApplicationSession`
           when called with an instance of :class:`autobahn.wamp.types.ComponentConfig`.
        :param start_reactor: When ``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.
        :param auto_reconnect:
        :param log_level:
        :param endpoint:
        :param reactor:
        :return: 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`
        """
        self.log.debug('{klass}.run()', klass=self.__class__.__name__)

        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=log_level)

        if callable(make):
            # factory for use ApplicationSession
            def create():
                cfg = ComponentConfig(self.realm, self.extra, runner=self)
                try:
                    session = make(cfg)
                except Exception:
                    self.log.failure(
                        'ApplicationSession could not be instantiated: {log_failure.value}'
                    )
                    if start_reactor and reactor.running:
                        reactor.stop()
                    raise
                else:
                    return session
        else:
            create = make

        if self.url.startswith('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-deflate responses from the server
            def accept(response):
                if isinstance(response, PerMessageDeflateResponse):
                    return PerMessageDeflateResponseAccept(response)

            # default WebSocket options for all client connections
            protocol_options = {
                'version': WebSocketProtocol.DEFAULT_SPEC_VERSION,
                'utf8validateIncoming': True,
                'acceptMaskedServerFrames': False,
                'maskClientFrames': True,
                'applyMask': True,
                'maxFramePayloadSize': 1048576,
                'maxMessagePayloadSize': 1048576,
                'autoFragmentSize': 65536,
                'failByDrop': True,
                'echoCloseCodeReason': False,
                'serverConnectionDropTimeout': 1.,
                'openHandshakeTimeout': 2.5,
                'closeHandshakeTimeout': 1.,
                'tcpNoDelay': True,
                'perMessageCompressionOffers': offers,
                'perMessageCompressionAccept': accept,
                'autoPingInterval': 10.,
                'autoPingTimeout': 5.,
                'autoPingSize': 12,

                # see: https://github.com/crossbario/autobahn-python/issues/1327 and
                # _cancelAutoPingTimeoutCall
                'autoPingRestartOnAnyTraffic': True,
            }

            # let user override above default options
            if self.websocket_options:
                protocol_options.update(self.websocket_options)

            # set websocket protocol options on Autobahn/Twisted protocol factory, from where it will
            # be applied for every Autobahn/Twisted protocol instance from the factory
            transport_factory.setProtocolOptions(**protocol_options)

        # supress pointless log noise
        transport_factory.noisy = False

        if endpoint:
            client = endpoint
        else:
            # 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)

        # 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):
            self._connect_successes += 1
            reactor.addSystemEventTrigger('before', 'shutdown', cleanup, proto)
            return proto

        use_service = False
        if auto_reconnect:
            try:
                # since Twisted 16.1.0
                from twisted.application.internet import ClientService
                from twisted.application.internet import backoffPolicy
                use_service = True
            except ImportError:
                use_service = False

        if use_service:
            # this code path is automatically reconnecting ..
            self.log.debug('using t.a.i.ClientService')

            if (self.max_retries is not None
                    or self.initial_retry_delay is not None
                    or self.max_retry_delay is not None
                    or self.retry_delay_growth is not None
                    or self.retry_delay_jitter is not None):

                if self.max_retry_delay > 0:
                    kwargs = {}

                    def _jitter():
                        j = 1 if self.retry_delay_jitter is None else self.retry_delay_jitter
                        return random.random() * j

                    for key, val in [('initialDelay',
                                      self.initial_retry_delay),
                                     ('maxDelay', self.max_retry_delay),
                                     ('factor', self.retry_delay_growth),
                                     ('jitter', _jitter)]:
                        if val is not None:
                            kwargs[key] = val

                    # retry policy that will only try to reconnect if we connected
                    # successfully at least once before (so it fails on host unreachable etc ..)
                    def retry(failed_attempts):
                        if self._connect_successes > 0 and (
                                self.max_retries == -1
                                or failed_attempts < self.max_retries):
                            return backoffPolicy(**kwargs)(failed_attempts)
                        else:
                            print('hit stop')
                            self.stop()
                            return 100000000000000
                else:
                    # immediately reconnect (zero delay)
                    def retry(_):
                        return 0
            else:
                retry = backoffPolicy()

            # https://twistedmatrix.com/documents/current/api/twisted.application.internet.ClientService.html
            self._client_service = ClientService(client,
                                                 transport_factory,
                                                 retryPolicy=retry)
            self._client_service.startService()

            d = self._client_service.whenConnected()

        else:
            # this code path is only connecting once!
            self.log.debug('using t.i.e.connect()')

            d = client.connect(transport_factory)

        # 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 the ApplicationSession sets an "error" key on the self.config.extra dictionary, which
            # has been set to the self.extra dictionary, extract the Exception from that and re-raise
            # it as the very last one (see below) exciting back to the caller of self.run()
            app_error = self.extra.get('error', None)

            # if we exited due to a connection error, raise that to the caller
            if connect_error.exception:
                raise connect_error.exception
            elif app_error:
                raise app_error

        else:
            # let the caller handle any errors
            return d