예제 #1
0
class NonRootAPI(object):
    """
    An API that will check for a Non Root API's
    """
    def __init__(self, comp):
        """
        A NonRootAPi constructor will init a logger and a comp property for an object
        that have to be compare with the existing registered protocols

        :param comp:
        :type comp: an input object as DynamicProtocol

        :return: void
        """

        self.comp = comp
        self.__logger = Logger()

    def check(self):
        """
        Will trying to match the candidate Non Root API
        check IJSONResourceAPI for more info

        :return: False if not success otherwise the response from matched API.method()
        """

        for api in subscribers([self.comp], IJSONResourceAPI):

            if api.__class__.__name__.lower() in self.comp.to_dict().keys():

                self.__logger.debug('Candidate API {} for {}'.format(
                    api.__class__.__name__, self.comp.__class__.__name__))

                candidate_api_name = self.comp.to_dict().get(
                    api.__class__.__name__.lower())

                try:
                    # execute the candidate API method
                    # and return the result
                    candidate_api_result = getattr(api, candidate_api_name)()

                except AttributeError as e:

                    msg = 'Candidate API {} for {} does not implement method {} error: {}'

                    self.__logger.warning(
                        msg.format(api.__class__.__name__,
                                   self.comp.__class__.__name__,
                                   candidate_api_name, e.message))

                else:

                    self.__logger.info('Successful apply API {} for {}'.format(
                        api.__class__.__name__, self.comp.__class__.__name__))

                    return candidate_api_result

        return False
예제 #2
0
def on_connect(self):
    """
    A function that is called when we got connected to a WAMP router.
    This function is attached to our WampDefaultComponent only if protocol is WSS
    :param self:
    :return: void
    """
    cfg = Config().get_wamp()
    log = Logger()

    log.info('Connecting to router...')

    self.join(self.config.realm, [u'wampcra'], cfg.user)
예제 #3
0
def on_connect(self):
    """
    A function that is called when we got connected to a WAMP router.
    This function is attached to our WampDefaultComponent only if protocol is WSS
    :param self:
    :return: void
    """
    cfg = Config().get_wamp()
    log = Logger()

    log.info('Connecting to router...')

    self.join(self.config.realm, [u'wampcra'], cfg.user)
예제 #4
0
class RootAPI(object):
    """
    An API that implements an IJSONResourceRootAPI
    """
    def __init__(self, comp):

        """
        A RootAPI constructor will init an logger and will initialize a public attribute
        - self.comp which representing an input object as DynamicProtocol

        :param comp:
        :type comp: DynamicProtocol

        :return: void
        """
        self.comp = comp
        self.__logger = Logger()

    def check(self):

        """
        Will trying to get a RootAPI and if match one will fired a process_factory
        of an implementation API

        :return: False if not success otherwise the response from process_factory
        """
        for api in subscribers([self.comp], IJSONResourceRootAPI):

            if api.__class__.__name__.lower() in self.comp.to_dict().keys():

                self.__logger.debug('Candidate API {} for {}'.format(
                    api.__class__.__name__,
                    self.comp.__class__.__name__
                ))

                self.__logger.info('Successful apply API {} for {}'.format(
                    api.__class__.__name__,
                    self.comp.__class__.__name__
                ))

                return api.process_factory()

        return False
예제 #5
0
def on_challenge(self, challenge):

    """
    A function that is called when we got onChallenge event aka authentication to a WAMP router.
    This function is attached to our WampDefaultComponent only if protocol is WSS

    :param self:
    :param challenge:

    :return: digital signature decode in ascii
    """

    log = Logger()
    log.info('On Challenge...')

    if challenge.method == u"wampcra":

        cfg = Config().get_wamp()

        password = {
            u'%s' % cfg.user: u'%s' % cfg.password
        }

        if u'salt' in challenge.extra:

            key = auth.derive_key(
                password[cfg.user].encode('utf8'),
                challenge.extra['salt'].encode('utf8'),
                challenge.extra.get('iterations', None),
                challenge.extra.get('keylen', None)
            )

        else:

            key = password[cfg.user].encode('utf8')
        
        signature = auth.compute_wcs(key, challenge.extra['challenge'].encode('utf8'))

        return signature.decode('ascii')

    else:

        raise Exception("don't know how to compute challenge for authmethod {}".format(challenge.method))
예제 #6
0
def on_challenge(self, challenge):
    """
    A function that is called when we got onChallenge event aka authentication to a WAMP router.
    This function is attached to our WampDefaultComponent only if protocol is WSS

    :param self:
    :param challenge:

    :return: digital signature decode in ascii
    """

    log = Logger()
    log.info('On Challenge...')

    if challenge.method == u"wampcra":

        cfg = Config().get_wamp()

        password = {u'%s' % cfg.user: u'%s' % cfg.password}

        if u'salt' in challenge.extra:

            key = auth.derive_key(password[cfg.user].encode('utf8'),
                                  challenge.extra['salt'].encode('utf8'),
                                  challenge.extra.get('iterations', None),
                                  challenge.extra.get('keylen', None))

        else:

            key = password[cfg.user].encode('utf8')

        signature = auth.compute_wcs(
            key, challenge.extra['challenge'].encode('utf8'))

        return signature.decode('ascii')

    else:

        raise Exception(
            "don't know how to compute challenge for authmethod {}".format(
                challenge.method))
예제 #7
0
class Reconnect(object):
    """
    A reconnect class
    """
    def __init__(self, **kwargs):
        """
        A constructor will init our wamp session and a runner.
        A runner is a callable that have to be processed when we lose the connection

        :param kwargs: keys: 'session' and 'runner'
        :return: void
        """

        if 'session' not in kwargs or 'runner' not in kwargs:
            raise Exception('session is not provided')

        self.__session = kwargs['session']
        self.__runner = kwargs['runner']

        self.logger = Logger()
        self.config = kwargs.get('config')

    def start(self):
        """
        Start the reconnection
        :return: void
        """
        try:

            self.logger.info(
                'TRYING TO CONNECT TO {}'.format(
                    self.config
                )
            )

            run = self.__runner(config=self.config)
            run.run(self.__session)

        except Exception as e:
            self.logger.error('RECONNECTING ERROR: {}'.format(e.message))
예제 #8
0
class RootAPI(object):
    """
    An API that implements an IJSONResourceRootAPI
    """
    def __init__(self, comp):
        """
        A RootAPI constructor will init an logger and will initialize a public attribute
        - self.comp which representing an input object as DynamicProtocol

        :param comp:
        :type comp: DynamicProtocol

        :return: void
        """
        self.comp = comp
        self.__logger = Logger()

    def check(self):
        """
        Will trying to get a RootAPI and if match one will fired a process_factory
        of an implementation API

        :return: False if not success otherwise the response from process_factory
        """
        for api in subscribers([self.comp], IJSONResourceRootAPI):

            if api.__class__.__name__.lower() in self.comp.to_dict().keys():

                self.__logger.debug('Candidate API {} for {}'.format(
                    api.__class__.__name__, self.comp.__class__.__name__))

                self.__logger.info('Successful apply API {} for {}'.format(
                    api.__class__.__name__, self.comp.__class__.__name__))

                return api.process_factory()

        return False
예제 #9
0
class Reconnect(object):
    """
    A reconnect class
    """
    def __init__(self, **kwargs):
        """
        A constructor will init our wamp session and a runner.
        A runner is a callable that have to be processed when we lose the connection

        :param kwargs: keys: 'session' and 'runner'
        :return: void
        """

        if 'session' not in kwargs or 'runner' not in kwargs:
            raise Exception('session is not provided')

        self.__session = kwargs['session']
        self.__runner = kwargs['runner']

        self.logger = Logger()
        self.config = kwargs.get('config')

    def start(self):
        """
        Start the reconnection
        :return: void
        """
        try:

            self.logger.info('TRYING TO CONNECT TO {}'.format(self.config))

            run = self.__runner(config=self.config)
            run.run(self.__session)

        except Exception as e:
            self.logger.error('RECONNECTING ERROR: {}'.format(e.message))
예제 #10
0
class DefaultLineReceiver(LineReceiver):
    """
    A default implementation of Twisted LineReceiver protocol
    """
    def __init__(self):
        """
        A constructor will init the logger only
        :return: void
        """
        self.__logger = Logger()

    def connectionMade(self):
        """
        A callback method that is fired when connection is made
        :return: void
        """
        self.__logger.info('Connections made')

    def connectionLost(self, reason='unexpected'):
        """
        A callback method that is fired when connection is lost
        :param reason:
        :return: void
        """
        self.__logger.info('Connection was closesd: {}'.format(reason))

    def lineReceived(self, line):
        """
        A callback method that is fired when live is received.
        Will validate, dispatch and choose the right API on the fly
        :param line: user input
        :type line: JSON
        :return: void
        """
        self.__logger.info('Received line: {}'.format(line))

        result = IDispatcher(Validator(line)).dispatch()
        result_helper = DispathcherResultHelper(result)

        result_helper.result_validation(
            self.sendLine,
            self.transport.loseConnection,
            'TCP'
        )
예제 #11
0
class DefaultLineReceiver(LineReceiver):
    """
    A default implementation of Twisted LineReceiver protocol
    """
    def __init__(self):
        """
        A constructor will init the logger only
        :return: void
        """
        self.__logger = Logger()

    def connectionMade(self):
        """
        A callback method that is fired when connection is made
        :return: void
        """
        self.__logger.info('Connections made')

    def connectionLost(self, reason='unexpected'):
        """
        A callback method that is fired when connection is lost
        :param reason:
        :return: void
        """
        self.__logger.info('Connection was closesd: {}'.format(reason))

    def lineReceived(self, line):
        """
        A callback method that is fired when live is received.
        Will validate, dispatch and choose the right API on the fly
        :param line: user input
        :type line: JSON
        :return: void
        """
        self.__logger.info('Received line: {}'.format(line))

        result = IDispatcher(Validator(line)).dispatch()
        result_helper = DispathcherResultHelper(result)

        result_helper.result_validation(self.sendLine,
                                        self.transport.loseConnection, 'TCP')
예제 #12
0
class AutobahnDefaultFactory(service.Service):
    """
    This class is a convenience tool mainly for development and quick hosting
    of WAMP application components.

    It can host a WAMP application component in a WAMP-over-WebSocket client
    connecting to a WAMP router.
    """

    def __init__(self, **kwargs):
        """

        :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 debug: Turn on low-level debugging.
        :type debug: bool
        :param debug_wamp: Turn on WAMP-level debugging.
        :type debug_wamp: bool
        :param debug_app: Turn on app-level debugging.
        :type debug_app: bool

        """

        self.logger = Logger()

        self.config = kwargs.get('config', None)

        if self.config is None:

            self.config = {}
            wamp = WAMP()

            self.logger.warning('Config is not provided failback to defaults')

            self.config.update({
                'protocol': 'wss',
                'hostname': 'localhost',
                'port': 8080, # integer
                'realm': 'realm1',
                'path': 'ws',
                'retry_interval': 2,  # in seconds
                'url': u'ws://localhost:8080/ws',
                'service_name': 'A Default WAMP Service name"'
            })

            self.config = wamp.to_object(self.config)

        self.url = kwargs.get('url', self.config.url)

        self.realm = u'{}'.format(kwargs.get('realm', self.config.realm))

        self.extra = kwargs.get('extra', dict())

        self.debug = kwargs.get('debug', False)

        self.debug_wamp = kwargs.get('debug_wamp', False)

        self.debug_app = kwargs.get('debug_app', False)

        self.belong_to = kwargs.get('belong_to', False)

        self.make = None

        self.protocol = kwargs.get('protocol', self.config.protocol)

        self.name = kwargs.get('name', self.config.service_name)

        self.port = kwargs.get('port', self.config.port)

        self.host = kwargs.get('host', self.config.hostname)

        self.path = kwargs.get('path', self.config.path)

    def run(self, make):
        """
        Run the application component.

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

        is_secure, host, port, resource, path, params = parse_url(self.url)

        # start logging to console
        if self.debug or self.debug_wamp or self.debug_app:
            pass
            # log.startLogging(sys.stdout)

        # factory for use ApplicationSession
        def create():
            """
            Will trying to create an ApplicationSession object
            :return: ApplicationSession
            """
            cfg = ComponentConfig(self.realm, self.extra)

            try:
                session = make(config=cfg)

            except Exception as e:

                # the app component could not be created .. fatal
                self.logger.critical('CREATE RUNNER EXCEPTION {}'.format(e.message))

            else:

                session.debug_app = self.debug_app
                return session

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

        if is_secure:
            endpoint_descriptor = "ssl:{0}:{1}".format(host, port)

        else:
            endpoint_descriptor = "tcp:{0}:{1}".format(host, port)

        try:
            self.logger.info('Trying to connect to: {}'.format(endpoint_descriptor))
            self.connect(endpoint_descriptor, transport_factory, make)

            return self

        except Exception as e:
            self.logger.error('CLIENT CONNECT ERROR: {}'.format(e.message))

    def connect(self, endpoint, transport, session):
        """
        Will make a connection to a WAMP router based on a Twisted endpoint
        :param endpoint:
        :param transport:
        :param session:
        :return:
        """
        try:

            # start the client from a Twisted endpoint

            client = clientFromString(reactor, endpoint)

            res = client.connect(transport)

            def connect_result(result):
                """
                A callback used as defer result
                :param result:
                :return:
                """
                return result

            def connect_error(result):
                """
                A callback used as defer if error occur
                :param result:
                :return:
                """
                self.logger.error('CONNECTION ERROR: {}'.format(result.getErrorMessage()))

                try:

                    reconnect = Reconnect(
                        session=session,
                        runner=AutobahnDefaultFactory,
                        config=self.config
                    )

                    reactor.callLater(
                        self.config.retry_interval,
                        reconnect.start,
                    )

                except Exception as e:
                    self.logger.error('RECONNECTING ERROR: {}'.format(e.message))

            res.addCallback(connect_result)
            res.addErrback(connect_error)

            return res

        except Exception as e:
            self.logger.error('CONNECTION GLOBAL ERRORS: {}'.format(e.message))

    def startService(self):
        """
        Start a WAMP service
        :return:
        """
        service.Service.startService(self)

    def stopService(self):
        """
        Stop a WAMP service
        :return:
        """
        service.Service.stopService(self)
예제 #13
0
class WampDefaultComponent(ApplicationSession):
    """
    Class that represent our default WAMP component we inherit from
    autobahn.twisted.wamp.ApplicationSession
    """
    def __init__(self, **kwargs):
        """
        The constructor will init self.cfg self.__gsm and if the protocol is wss,
        will attach on_connect and on_challenge to this object as methods
        :param kwargs: we looking for key 'config'
        :return: void
        """
        config = kwargs.get('config')

        super(WampDefaultComponent, self).__init__(config)

        self.__logger = Logger()
        self.cfg = Config().get_wamp()

        self.__gsm = getGlobalSiteManager()

        if self.cfg.protocol == 'wss':

            self.__logger.info('WAMP is secure, switch to wss...')

            WampDefaultComponent.onConnect = on_connect
            WampDefaultComponent.onChallenge = on_challenge

    def subscriber_dispatcher(self, sub_data):
        """
        Callback function for our global subscriber
        will pass sub_data to GlobalSubscribeMessage and then will trying to
        get wamp component which implements IUserGlobalSubscriber and adapts
        IGlobalSubscribeMessage

        :param sub_data:
        """

        log = Logger()

        try:

            result = IDispatcher(Validator(sub_data)).dispatch()

        # NETODO what exception can happen here?
        except Exception as e:

            import traceback
            print traceback.format_exc()

            log.warning('subscriber_dispatcher exception: {}'.format(
                e.message))

        else:

            if IValidator.providedBy(result):
                log.warning('WAMP Message is invalid: {}'.format(
                    result.message))

            if result is not False and IJSONResource.providedBy(result):

                fac = None

                for sub in subscribers([result], IUserGlobalSubscriber):

                    sub.subscribe(self)
                    fac = True

                    break

                if not fac:
                    log.warning(
                        'There are no user definition for IUserGlobalSubscriber, message was skipped'
                    )

    @inlineCallbacks
    def onJoin(self, details):
        """
        Autobahn callback function that fired when onJoin happens (we join to our realm)

        Here all components recognized as WAMP we will registering on the fly.

        The global subscriber will be started too, its name is formed by
        netcatks_global_subscriber_ + WAMP Service name.

        Also inside our Storage register will be saved the current WAMP session object
        in __wamp_session__ namespace.

        If there an API which providing a IWAMPLoadOnRunTime implementation will be started on
        run time

        :param details:
        :return:
        """
        self.__logger.info('WAMP Session is ready')

        sub_topic = 'netcatks_global_subscriber_{}'.format(
            self.cfg.service_name.lower().replace(' ', '_'))

        yield self.subscribe(self.subscriber_dispatcher, sub_topic)
        self.__logger.info('Starting global subscriber: {}'.format(sub_topic))

        # registration of all classes which ends with Wamp into shared wamp session
        for x in list(self.__gsm.registeredSubscriptionAdapters()):

            # storing wamp session inside storage
            sr = createObject('storageregister').components
            sr['__wamp_session__'] = self

            if IWAMPResource in x.required:

                if x.factory.__name__ != 'BaseWampComponent':

                    f = x.factory()

                    self.__logger.info('Register Wamp Component: {}'.format(
                        f.__class__.__name__))

                    yield self.register(f)

                    # here we provide wamp session to each wamp component,
                    # in this way every component can access methods like publish, call etc,
                    # which are hosted by default inside wamp session.

                    f.set_session(self)

            if x.provided is IWAMPLoadOnRunTime:
                x.factory('init').load()

    def onDisconnect(self):
        """
        Will be fired when we go a disconnecting from a WAMP router
        :return:
        """
        self.__logger.warning('Disconnected...')

        try:

            reconnect = Reconnect(session=WampDefaultComponent,
                                  runner=AutobahnDefaultFactory,
                                  config=self.cfg)

            reactor.callLater(
                self.cfg.retry_interval,
                reconnect.start,
            )

        # NETODO what exception trow here?
        except Exception as e:
            self.__logger.warning('disconnect warn: {}'.format(e.message))
예제 #14
0
class DispathcherResultHelper(object):

    """
    A helper class that care about of the result and the response of the request
    """

    def __init__(self, factory):

        """
        Just settings
        :param factory:
        :return: void
        """

        self.__logger = Logger()
        self.factory = factory

    @staticmethod
    def deferred_response(response, sender):

        """
        Care about global defered response
        :param response: response from defer operation
        :param sender: TBA

        :return: void
        """
        if sender is not None:
            sender(response.to_json())

    def deferred_response_error(self, err):

        """
        fired if global defered response fail
        :param err:

        :return: False
        """
        self.__logger.error('Cannot send message to user: {}'.format(
            err
        ))

        return False

    def result_validation(self, sender=None, drop=None, section='TCP'):

        """
        A Result Helper more info TBA

        :param sender:
        :param drop:
        :param section:

        :return: mixin
        """

        if IValidator.providedBy(self.factory):

            self.__logger.warning('{} Message is invalid: {}'.format(section, self.factory.message))

            if drop is not None:
                drop()

            else:
                return 'message is invalid'

        else:

            if self.factory:

                self.__logger.info('{} Response: {}'.format(section, self.factory))

                if IJSONResource.providedBy(self.factory):

                    if sender is not None:
                        sender(self.factory.to_json())

                    else:
                        return self.factory.to_json()

                elif isinstance(self.factory, Deferred):

                    self.factory.addCallback(self.deferred_response, sender)
                    self.factory.addErrback(self.deferred_response_error)

            else:

                self.__logger.warning('{}: This message was not be dispatched'.format(section))
                drop()
예제 #15
0
class NonRootAPI(object):

    """
    An API that will check for a Non Root API's
    """

    def __init__(self, comp):

        """
        A NonRootAPi constructor will init a logger and a comp property for an object
        that have to be compare with the existing registered protocols

        :param comp:
        :type comp: an input object as DynamicProtocol

        :return: void
        """

        self.comp = comp
        self.__logger = Logger()

    def check(self):

        """
        Will trying to match the candidate Non Root API
        check IJSONResourceAPI for more info

        :return: False if not success otherwise the response from matched API.method()
        """

        for api in subscribers([self.comp], IJSONResourceAPI):

            if api.__class__.__name__.lower() in self.comp.to_dict().keys():

                self.__logger.debug('Candidate API {} for {}'.format(
                    api.__class__.__name__,
                    self.comp.__class__.__name__
                ))

                candidate_api_name = self.comp.to_dict().get(api.__class__.__name__.lower())

                try:
                    # execute the candidate API method
                    # and return the result
                    candidate_api_result = getattr(api, candidate_api_name)()

                except AttributeError as e:

                    msg = 'Candidate API {} for {} does not implement method {} error: {}'

                    self.__logger.warning(msg.format(
                        api.__class__.__name__,
                        self.comp.__class__.__name__,
                        candidate_api_name,
                        e.message
                    ))

                else:

                    self.__logger.info('Successful apply API {} for {}'.format(
                        api.__class__.__name__,
                        self.comp.__class__.__name__
                    ))

                    return candidate_api_result

        return False
예제 #16
0
class AutobahnDefaultFactory(service.Service):
    """
    This class is a convenience tool mainly for development and quick hosting
    of WAMP application components.

    It can host a WAMP application component in a WAMP-over-WebSocket client
    connecting to a WAMP router.
    """
    def __init__(self, **kwargs):
        """

        :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 debug: Turn on low-level debugging.
        :type debug: bool
        :param debug_wamp: Turn on WAMP-level debugging.
        :type debug_wamp: bool
        :param debug_app: Turn on app-level debugging.
        :type debug_app: bool

        """

        self.logger = Logger()

        self.config = kwargs.get('config', None)

        if self.config is None:

            self.config = {}
            wamp = WAMP()

            self.logger.warning('Config is not provided failback to defaults')

            self.config.update({
                'protocol': 'wss',
                'hostname': 'localhost',
                'port': 8080,  # integer
                'realm': 'realm1',
                'path': 'ws',
                'retry_interval': 2,  # in seconds
                'url': u'ws://localhost:8080/ws',
                'service_name': 'A Default WAMP Service name"'
            })

            self.config = wamp.to_object(self.config)

        self.url = kwargs.get('url', self.config.url)

        self.realm = u'{}'.format(kwargs.get('realm', self.config.realm))

        self.extra = kwargs.get('extra', dict())

        self.debug = kwargs.get('debug', False)

        self.debug_wamp = kwargs.get('debug_wamp', False)

        self.debug_app = kwargs.get('debug_app', False)

        self.belong_to = kwargs.get('belong_to', False)

        self.make = None

        self.protocol = kwargs.get('protocol', self.config.protocol)

        self.name = kwargs.get('name', self.config.service_name)

        self.port = kwargs.get('port', self.config.port)

        self.host = kwargs.get('host', self.config.hostname)

        self.path = kwargs.get('path', self.config.path)

    def run(self, make):
        """
        Run the application component.

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

        is_secure, host, port, resource, path, params = parse_url(self.url)

        # start logging to console
        if self.debug or self.debug_wamp or self.debug_app:
            pass
            # log.startLogging(sys.stdout)

        # factory for use ApplicationSession
        def create():
            """
            Will trying to create an ApplicationSession object
            :return: ApplicationSession
            """
            cfg = ComponentConfig(self.realm, self.extra)

            try:
                session = make(config=cfg)

            except Exception as e:

                # the app component could not be created .. fatal
                self.logger.critical('CREATE RUNNER EXCEPTION {}'.format(
                    e.message))

            else:

                session.debug_app = self.debug_app
                return session

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

        if is_secure:
            endpoint_descriptor = "ssl:{0}:{1}".format(host, port)

        else:
            endpoint_descriptor = "tcp:{0}:{1}".format(host, port)

        try:
            self.logger.info(
                'Trying to connect to: {}'.format(endpoint_descriptor))
            self.connect(endpoint_descriptor, transport_factory, make)

            return self

        except Exception as e:
            self.logger.error('CLIENT CONNECT ERROR: {}'.format(e.message))

    def connect(self, endpoint, transport, session):
        """
        Will make a connection to a WAMP router based on a Twisted endpoint
        :param endpoint:
        :param transport:
        :param session:
        :return:
        """
        try:

            # start the client from a Twisted endpoint

            client = clientFromString(reactor, endpoint)

            res = client.connect(transport)

            def connect_result(result):
                """
                A callback used as defer result
                :param result:
                :return:
                """
                return result

            def connect_error(result):
                """
                A callback used as defer if error occur
                :param result:
                :return:
                """
                self.logger.error('CONNECTION ERROR: {}'.format(
                    result.getErrorMessage()))

                try:

                    reconnect = Reconnect(session=session,
                                          runner=AutobahnDefaultFactory,
                                          config=self.config)

                    reactor.callLater(
                        self.config.retry_interval,
                        reconnect.start,
                    )

                except Exception as e:
                    self.logger.error('RECONNECTING ERROR: {}'.format(
                        e.message))

            res.addCallback(connect_result)
            res.addErrback(connect_error)

            return res

        except Exception as e:
            self.logger.error('CONNECTION GLOBAL ERRORS: {}'.format(e.message))

    def startService(self):
        """
        Start a WAMP service
        :return:
        """
        service.Service.startService(self)

    def stopService(self):
        """
        Stop a WAMP service
        :return:
        """
        service.Service.stopService(self)
예제 #17
0
class WampDefaultComponent(ApplicationSession):

    """
    Class that represent our default WAMP component we inherit from
    autobahn.twisted.wamp.ApplicationSession
    """

    def __init__(self, **kwargs):
        """
        The constructor will init self.cfg self.__gsm and if the protocol is wss,
        will attach on_connect and on_challenge to this object as methods
        :param kwargs: we looking for key 'config'
        :return: void
        """
        config = kwargs.get('config')

        super(WampDefaultComponent, self).__init__(config)

        self.__logger = Logger()
        self.cfg = Config().get_wamp()

        self.__gsm = getGlobalSiteManager()

        if self.cfg.protocol == 'wss':

            self.__logger.info('WAMP is secure, switch to wss...')

            WampDefaultComponent.onConnect = on_connect
            WampDefaultComponent.onChallenge = on_challenge

    def subscriber_dispatcher(self, sub_data):

        """
        Callback function for our global subscriber
        will pass sub_data to GlobalSubscribeMessage and then will trying to
        get wamp component which implements IUserGlobalSubscriber and adapts
        IGlobalSubscribeMessage

        :param sub_data:
        """

        log = Logger()

        try:

            result = IDispatcher(Validator(sub_data)).dispatch()

        # NETODO what exception can happen here?
        except Exception as e:

            import traceback
            print traceback.format_exc()

            log.warning('subscriber_dispatcher exception: {}'.format(
                e.message
            ))

        else:

            if IValidator.providedBy(result):
                log.warning('WAMP Message is invalid: {}'.format(result.message))

            if result is not False and IJSONResource.providedBy(result):

                fac = None

                for sub in subscribers([result], IUserGlobalSubscriber):

                    sub.subscribe(self)
                    fac = True

                    break

                if not fac:
                    log.warning('There are no user definition for IUserGlobalSubscriber, message was skipped')



    @inlineCallbacks
    def onJoin(self, details):
        """
        Autobahn callback function that fired when onJoin happens (we join to our realm)

        Here all components recognized as WAMP we will registering on the fly.

        The global subscriber will be started too, its name is formed by
        netcatks_global_subscriber_ + WAMP Service name.

        Also inside our Storage register will be saved the current WAMP session object
        in __wamp_session__ namespace.

        If there an API which providing a IWAMPLoadOnRunTime implementation will be started on
        run time

        :param details:
        :return:
        """
        self.__logger.info('WAMP Session is ready')

        sub_topic = 'netcatks_global_subscriber_{}'.format(
            self.cfg.service_name.lower().replace(' ', '_')
        )

        yield self.subscribe(self.subscriber_dispatcher, sub_topic)
        self.__logger.info('Starting global subscriber: {}'.format(sub_topic))

        # registration of all classes which ends with Wamp into shared wamp session
        for x in list(self.__gsm.registeredSubscriptionAdapters()):

            # storing wamp session inside storage
            sr = createObject('storageregister').components
            sr['__wamp_session__'] = self

            if IWAMPResource in x.required:

                if x.factory.__name__ != 'BaseWampComponent':

                    f = x.factory()

                    self.__logger.info('Register Wamp Component: {}'.format(f.__class__.__name__))

                    yield self.register(f)

                    # here we provide wamp session to each wamp component,
                    # in this way every component can access methods like publish, call etc,
                    # which are hosted by default inside wamp session.

                    f.set_session(self)

            if x.provided is IWAMPLoadOnRunTime:
                x.factory('init').load()

    def onDisconnect(self):

        """
        Will be fired when we go a disconnecting from a WAMP router
        :return:
        """
        self.__logger.warning('Disconnected...')

        try:

            reconnect = Reconnect(
                session=WampDefaultComponent,
                runner=AutobahnDefaultFactory,
                config=self.cfg
            )

            reactor.callLater(
                self.cfg.retry_interval,
                reconnect.start,
            )

        # NETODO what exception trow here?
        except Exception as e:
            self.__logger.warning('disconnect warn: {}'.format(e.message))
예제 #18
0
class DispathcherResultHelper(object):
    """
    A helper class that care about of the result and the response of the request
    """
    def __init__(self, factory):
        """
        Just settings
        :param factory:
        :return: void
        """

        self.__logger = Logger()
        self.factory = factory

    @staticmethod
    def deferred_response(response, sender):
        """
        Care about global defered response
        :param response: response from defer operation
        :param sender: TBA

        :return: void
        """
        if sender is not None:
            sender(response.to_json())

    def deferred_response_error(self, err):
        """
        fired if global defered response fail
        :param err:

        :return: False
        """
        self.__logger.error('Cannot send message to user: {}'.format(err))

        return False

    def result_validation(self, sender=None, drop=None, section='TCP'):
        """
        A Result Helper more info TBA

        :param sender:
        :param drop:
        :param section:

        :return: mixin
        """

        if IValidator.providedBy(self.factory):

            self.__logger.warning('{} Message is invalid: {}'.format(
                section, self.factory.message))

            if drop is not None:
                drop()

            else:
                return 'message is invalid'

        else:

            if self.factory:

                self.__logger.info('{} Response: {}'.format(
                    section, self.factory))

                if IJSONResource.providedBy(self.factory):

                    if sender is not None:
                        sender(self.factory.to_json())

                    else:
                        return self.factory.to_json()

                elif isinstance(self.factory, Deferred):

                    self.factory.addCallback(self.deferred_response, sender)
                    self.factory.addErrback(self.deferred_response_error)

            else:

                self.__logger.warning(
                    '{}: This message was not be dispatched'.format(section))
                drop()