def connect(self): parts = chop(self.options['endpoint'], sep=':') if parts[0] == 'serial': endpoint = parts[1:] self.protocol = self.factory.buildProtocol(0) try: self.serport = SerialPort(self.protocol, endpoint[0], reactor, baudrate=endpoint[1]) except Exception as e: self.log.error("{excp}", excp=e) yield self.stopService() else: self.gotProtocol(self.protocol) self.log.info("Using serial port {tty} at {baud} bps", tty=endpoint[0], baud=endpoint[1]) else: ClientService.startService(self) try: protocol = yield self.whenConnected(failAfterFailures=1) except Exception as e: self.log.error("{excp}", excp=e) yield self.stopService() else: self.gotProtocol(protocol) self.log.info("Using TCP endpoint {endpoint}", endpoint=self.options['endpoint'])
class _ControllerProxy(ExportedState): def __init__(self, reactor, endpoint, elements, encoding, clock=None): self.__reactor = reactor self.__elements = elements self.__encoding = encoding self.__client_service = ClientService( endpoint=endpoint, factory=Factory.forProtocol(_ControllerProtocol), clock=clock) self.__client_service.startService() def state_def(self): for d in super(_ControllerProxy, self).state_def(): yield d for element in self.__elements: for d in IElement(element)._cells(self.__send, self.__encoding): yield d def close(self): """implements IComponent""" self.__client_service.stopService() def attach_context(self, device_context): """implements IComponent""" @defer.inlineCallbacks def __send(self, cmd): protocol = yield self.__client_service.whenConnected(failAfterFailures=1) # returned deferred is not actually used but in the future it would be good if Commands did in fact have a 'done' signal defer.returnValue(protocol.send(cmd))
def __init__(self, options, **kargs): self.options = options self.topics = [] self.regAllowed = False self.validate = options['validation'] setLogLevel(namespace=NAMESPACE, levelStr=options['log_level']) setLogLevel(namespace=PROTOCOL_NAMESPACE, levelStr=options['protocol_log_level']) self.tess_heads = [ t.split('/')[0] for t in self.options['tess_topics'] ] self.tess_tails = [ t.split('/')[2] for t in self.options['tess_topics'] ] self.factory = MQTTFactory(profile=MQTTFactory.SUBSCRIBER) self.endpoint = clientFromString(reactor, self.options['broker']) if self.options['username'] == "": self.options['username'] = None self.options['password'] = None self.resetCounters() ClientService.__init__(self, self.endpoint, self.factory, retryPolicy=backoffPolicy( initialDelay=INITIAL_DELAY, factor=FACTOR, maxDelay=MAX_DELAY))
def __init__(self, endpoint, ident, secret, retryPolicy=None): super(ClientSessionService, self).__init__() self.ident = ident self.read_queue = defer.DeferredQueue() self.subscriptions = set() self.protocol = None from twisted.internet import reactor if isinstance(endpoint, str): self.client_endpoint = clientFromString(reactor, endpoint) elif hasattr(endpoint, 'connect'): self.client_endpoint = endpoint else: raise ValueError( 'endpoint must be a str or implement IStreamClientEndpoint') self.client_factory = ClientFactory.forProtocol( _Protocol, ident, secret) self.client_factory.service = self self.client_service = ClientService( self.client_endpoint, self.client_factory, retryPolicy=retryPolicy, ) self.client_service.setServiceParent(self) self.whenConnected = defer.Deferred()
def __init__(self, endpoint, factory, log, connections, name): ClientService.__init__(self, endpoint, factory) self.endpoint = endpoint self.factory = factory self.log = log self.connections = connections self.name = name
def connect(): endpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 8750) factory = Factory() factory.protocol = amp.AMP service = ClientService(endpoint, factory, retryPolicy=backoffPolicy(0.5, 15.0)) service.startService() return service.whenConnected()
class BitcoinDESubscribe(object): """Offers subscription services to the bitcoin.de websocket-API. Register Callback functions in case a market event occures. Opens a https connection. Mostly used as a proxy to the underlying subscription-aware factory.""" def __init__(self, reactor): self.reactor = reactor print "BitcoinDESubscribeFactory - constructor" tlsctx = optionsForClientTLS(u'ws.bitcoin.de') #,trustRoot=None) self.endpoint = endpoints.SSL4ClientEndpoint(self.reactor, 'ws.bitcoin.de', 443, tlsctx) self.factory = BitcoinDESubscribeFactory() self.connService = ClientService(self.endpoint, self.factory) self.connService.startService() # Proxies def SubscribeAdd(self, func): return self.factory.SubscribeAdd(func) def SubscribeRemove(self, func): return self.factory.SubscribeRemove(func) def SubscribeManagement(self, func): return self.factory.SubscribeManagement(func) def SubscribeUpdate(self, func): return self.factory.SubscribeUpdate(func)
def startService(self): """ Start the MQTT service """ print("starting MQTT Client Subscriber Service") # invoke whenConnected() inherited method self.whenConnected().addCallback(self.connectToBroker) ClientService.startService(self)
def __init__(self, endpoint, factory, observersByTopic, clientId): self.endpoint = endpoint self.observersByTopic = observersByTopic self.clientId = clientId ClientService.__init__(self, endpoint, factory, retryPolicy=lambda _: 5)
def __init__(self, endpoint, factory, devid, username, password): self.client = ClientService(endpoint, factory) self.connected = False self.devid = devid self.username = username self.password = password self.messages = []
def startService(self): log.info("Starting MQTT publisher plugin") # subscribe to AQI updates aqi_monitor = self.parent.getServiceNamed(aqimon.AqiMonitor.name) aqi_monitor.add_listener(self) # invoke whenConnected() inherited method self.whenConnected().addCallback(self.connectToBroker) ClientService.startService(self)
def __init(self, endpoint, factory): """ Create MQTT client service """ ClientService.__init__(self, endpoint, factory, retryPolicy=backoffPolicy())
def connect(self, host, port, reconnect=True): """Initiate outgoing connection, either persistent or no""" print("Initiating connection to %s:%d" % (host, port)) ep = TCP4ClientEndpoint(self._reactor, host, port) if reconnect: service = ClientService(ep, self, retryPolicy=lambda x: 1) service.startService() else: ep.connect(self)
def __init__(self, host, port, topic, user, password): self.broker_url = "tcp:%s:%s" % (host, port) self.topic = topic self.user = user self.password = password self.connected = False factory = MQTTFactory(profile=MQTTFactory.PUBLISHER) endpoint = clientFromString(reactor, self.broker_url) ClientService.__init__(self, endpoint, factory, retryPolicy=backoffPolicy())
def __init__(self, reactor): self.reactor = reactor factory = MQTTFactory(profile=MQTTFactory.PUBLISHER) endpoint = clientFromString(reactor, BROKER) ClientService.__init__(self, endpoint, factory, retryPolicy=backoffPolicy()) self.connected = False
def __init__(self, endpoint, factory, config, log, connections): ClientService.__init__(self, endpoint, factory, retryPolicy=backoffPolicy()) self.config = config self.log = log self.endpoint = endpoint self.connections = connections self.host = None self.port = None
def __init__(self, url=None, conn_string=None, authParams=None, reactor=None, **options): ''' Creates the client, but does not connect to the server automatically. Optional keyword parameters (**options) for... Client: url (required), authParams, reactor, conn_string, debug, factory protocol: url (required), authParams, heartbeat_interval rpc: rpcAckTimeout, rpcResponseTimeout, subscriptionTimeout record: recordReadAckTimeout, merge_strategy, recordReadTimeout, recordDeleteTimeout, recordDeepCopy, presence: subscriptionTimeout ''' if not url or url is None: raise ValueError( "url is None; you must specify a URL for the deepstream server, e.g. ws://localhost:6020/deepstream") parse_result = urlparse(url) if not authParams or authParams is None: authParams = {} if parse_result.username and parse_result.password: authParams['username'] = parse_result.username authParams['password'] = parse_result.password if not conn_string or conn_string is None: if parse_result.scheme == 'ws': if parse_result.hostname: conn_string = 'tcp:%s' % parse_result.hostname if parse_result.port: conn_string += ':%s' % parse_result.port else: conn_string += ':6020' if not conn_string or conn_string is None: raise ValueError( "Could not parse conn string from URL; you must specify a Twisted endpoint descriptor for the server, e.g. tcp:127.0.0.1:6020") if not reactor or reactor is None: from twisted.internet import reactor self.reactor = reactor factory = options.pop('factory', WSDeepstreamFactory) self._factory = factory(url, self, debug=options.pop('debug', False), reactor=reactor, **options) self._endpoint = clientFromString(reactor, conn_string) self._service = ClientService(self._endpoint, self._factory) # Handles reconnection for us EventEmitter.__init__(self) self._connection = ConnectionInterface(self, url) self._presence = PresenceHandler(self._connection, self, **options) self._event = EventHandler(self._connection, self, **options) self._rpc = RPCHandler(self._connection, self, **options) self._record = RecordHandler(self._connection, self, **options) self._message_callbacks = dict() self._message_callbacks[ constants.topic.PRESENCE] = self._presence.handle self._message_callbacks[ constants.topic.EVENT] = self._event.handle self._message_callbacks[ constants.topic.RPC] = self._rpc.handle self._message_callbacks[ constants.topic.RECORD] = self._record.handle self._message_callbacks[ constants.topic.ERROR] = self._on_error
def __init__(self, reactor, endpoint, elements, encoding, clock=None): self.__reactor = reactor self.__elements = elements self.__encoding = encoding self.__client_service = ClientService( endpoint=endpoint, factory=Factory.forProtocol(_ControllerProtocol), clock=clock) self.__client_service.startService()
def start(self): """Start connecting to ZenHub.""" self.__stopping = False factory = ZenPBClientFactory() self.__service = ClientService( self.__endpoint, factory, retryPolicy=backoffPolicy(initialDelay=0.5, factor=3.0), ) self.__service.startService() self.__prepForConnection()
def SetupConnection(self, host, port): self.__log.debug("Setting up connection! %s %s " % (host, port)) factory = Factory.forProtocol(NeoNode) endpoint = clientFromString(reactor,"tcp:host=%s:port=%s:timeout=5" % (host,port)) connectingService = ClientService( endpoint, factory, retryPolicy=backoffPolicy(.5, factor=3.0) ) connectingService.startService()
def main(): from twisted.internet import reactor, ssl # Simple 'TrackMarket' example displaying all events processed tlsctx = optionsForClientTLS(u'ws.bitcoin.de', None) endpoint = endpoints.SSL4ClientEndpoint(reactor, 'ws.bitcoin.de', 443, tlsctx) factory = BitcoinDEMarket() connService = ClientService(endpoint, factory) connService.startService() reactor.run()
def __init__(self, reactor): self.reactor = reactor print "BitcoinDESubscribeFactory - constructor" tlsctx = optionsForClientTLS(u'ws.bitcoin.de') #,trustRoot=None) self.endpoint = endpoints.SSL4ClientEndpoint(self.reactor, 'ws.bitcoin.de', 443, tlsctx) self.factory = BitcoinDESubscribeFactory() self.connService = ClientService(self.endpoint, self.factory) self.connService.startService()
def makeSubscriberService(endpoint, local_ivo, validators, handlers, filters): """Create a reconnecting VOEvent subscriber service. Parameters ---------- endpoint : implements `twisted.internet.interfaces.IStreamClientEndpoint` The endpoint to which the service will connect. local_ivo : `str` or `None` IVOA identifier for the subscriber. validators : `list` of implementers of `~comet.icomet.IValidator`. Validators which will be applied to incoming events. Events which fail validation will be rejected. handlers : `list` of implementers of `~comet.icomet.IHandler`. Handlers to which events which pass validation will be passed. filters : `list` of `str` XPath filters. Will be passed to upstream as a request to filter the alerts being sent. Notes ----- Upstream brokes may not provide support for XPath filtering; in this case, the filters suppplied will be ignored. Reconnection is handled according to the default policies of `twisted.application.internet.ClientService`. """ factory = VOEventSubscriberFactory(local_ivo, validators,handlers, filters) service = ClientService(endpoint, factory) return service
def getMapUpdaterComponentService(self, url, realm, mapUpdater): def create(config): try: session = MapUpdaterComponent(mapUpdater, config) except Exception as e: # the app component could not be created .. fatal print(e) else: session.debug_app = True return session sessionFactory = ApplicationSessionFactory( ComponentConfig(realm, None)) sessionFactory.session = create transportFactory = WampWebSocketClientFactory( sessionFactory, url=url) transportFactory.noisy = False transportFactory.autoPingInterval = 30 transportFactory.autoPingTimeout = 30 isSecure, host, port, resource, path, params = parseWsUrl(url) endpoint = HostnameEndpoint(reactor, host.encode('utf8'), port) if isSecure: contextFactory = optionsForClientTLS(hostname=host) endpoint = wrapClientTLS(contextFactory, endpoint) return ClientService(endpoint, transportFactory)
def test_wb_heartbeat(self): """ If heartbeat checks fail due to network issues, we keep re-trying until the network recovers. """ self.service.stopService() # Put a TCP proxy between NotificationSource and RabbitMQ, to simulate # packets getting dropped on the floor. proxy = ProxyService(self.rabbit.config.hostname, self.rabbit.config.port) proxy.startService() self.addCleanup(proxy.stopService) self.endpoint._port = proxy.port self.service = ClientService(self.endpoint, self.factory, retryPolicy=self.policy) self.connector._service = self.service self.service.startService() # This will make the connector setup the channel before we call # get(), so by the time we call it in the next line all # connector-related deferreds will fire synchronously and the # code will block on basic-consume. channel = yield self.connector() deferred = self.source.get("uuid", 0) # Start dropping packets on the floor proxy.block() # Publish a notification, which won't be delivered just yet. yield self.channel.basic_publish(routing_key="uuid", content=Content("hello")) # Wait for the first connection to terminate, because heartbeat # checks will fail. yield channel.client.disconnected.wait() # Now let packets flow again. proxy.unblock() # The situation got recovered. notification = yield deferred self.assertEqual("hello", notification.payload) self.assertEqual(2, proxy.connections)
def _makeService(self): """ Construct a service for the endpoint as described. """ factory = WebSocketClientFactory() factory.protocol = SlackProtocol factory.bot = self.bot return ClientService(self.bot, factory)
def __init__(self, options, **kargs): self.options = options setLogLevel(namespace=NAMESPACE, levelStr=options['log_level']) setLogLevel(namespace=PROTOCOL_NAMESPACE, levelStr=options['log_messages']) self.factory = MQTTFactory(profile=MQTTFactory.PUBLISHER) self.endpoint = clientFromString(reactor, self.options['broker']) self.task = None if self.options['username'] == "": self.options['username'] = None self.options['password'] = None ClientService.__init__(self, self.endpoint, self.factory, retryPolicy=backoffPolicy( initialDelay=INITIAL_DELAY, factor=FACTOR, maxDelay=MAX_DELAY)) self.queue = DeferredQueue()
def start(self): for server in cycle(self.config['default_servers']): endpoint = clientFromString(reactor, 'tcp:{}:{}'.format(*server)) self.service = ClientService(endpoint, StratumClientFactory(self)) self.service.startService() try: self.client = yield self.service.whenConnected( failAfterFailures=2) yield self.ensure_server_version() self._on_connected_controller.add(True) yield self.client.on_disconnected.first except CancelledError: return except Exception as e: pass finally: self.client = None if not self.running: return
def make_plugin(application, app_config): slack_config = config.parse_config(app_config, { "token": config.String, }) api_client = SlackWebClient(slack_config.token) endpoint = SlackEndpoint(api_client) plugin = SlackPlugin(api_client) factory = SlackClientFactory( plugin=plugin, useragent="Harold ([email protected])", ) factory.setProtocolOptions( autoPingInterval=5, autoPingTimeout=10, ) service = ClientService(endpoint, factory) service.setServiceParent(application) return plugin
def __init__( self, host: str, port: int, maximum_buffer: int = 1000, level=logging.NOTSET, _reactor=None, ): super().__init__(level=level) self.host = host self.port = port self.maximum_buffer = maximum_buffer self._buffer = deque() # type: Deque[logging.LogRecord] self._connection_waiter = None # type: Optional[Deferred] self._producer = None # type: Optional[LogProducer] # Connect without DNS lookups if it's a direct IP. if _reactor is None: from twisted.internet import reactor _reactor = reactor try: ip = ip_address(self.host) if isinstance(ip, IPv4Address): endpoint = TCP4ClientEndpoint( _reactor, self.host, self.port) # type: IStreamClientEndpoint elif isinstance(ip, IPv6Address): endpoint = TCP6ClientEndpoint(_reactor, self.host, self.port) else: raise ValueError("Unknown IP address provided: %s" % (self.host, )) except ValueError: endpoint = HostnameEndpoint(_reactor, self.host, self.port) factory = Factory.forProtocol(Protocol) self._service = ClientService(endpoint, factory, clock=_reactor) self._service.startService() self._stopping = False self._connect()
def test_wb_heartbeat(self): """ If heartbeat checks fail due to network issues, we keep re-trying until the network recovers. """ self.service.stopService() # Put a TCP proxy between NotificationSource and RabbitMQ, to simulate # packets getting dropped on the floor. proxy = ProxyService( self.rabbit.config.hostname, self.rabbit.config.port) proxy.startService() self.addCleanup(proxy.stopService) self.endpoint._port = proxy.port self.service = ClientService( self.endpoint, self.factory, retryPolicy=self.policy) self.connector._service = self.service self.service.startService() # This will make the connector setup the channel before we call # get(), so by the time we call it in the next line all # connector-related deferreds will fire synchronously and the # code will block on basic-consume. channel = yield self.connector() deferred = self.source.get("uuid", 0) # Start dropping packets on the floor proxy.block() # Publish a notification, which won't be delivered just yet. yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) # Wait for the first connection to terminate, because heartbeat # checks will fail. yield channel.client.disconnected.wait() # Now let packets flow again. proxy.unblock() # The situation got recovered. notification = yield deferred self.assertEqual("hello", notification.payload) self.assertEqual(2, proxy.connections)
def setUp(self): super(NotificationSourceIntegrationTest, self).setUp() self.endpoint = AMQEndpoint( reactor, self.rabbit.config.hostname, self.rabbit.config.port, username="******", password="******", heartbeat=1) self.policy = backoffPolicy(initialDelay=0) self.factory = AMQFactory(spec=AMQP0_8_SPEC_PATH) self.service = ClientService( self.endpoint, self.factory, retryPolicy=self.policy) self.connector = NotificationConnector(self.service) self.source = NotificationSource(self.connector) self.client = yield self.endpoint.connect(self.factory) self.channel = yield self.client.channel(1) yield self.channel.channel_open() yield self.channel.queue_declare(queue="uuid") self.service.startService()
def startService(self): self.whenConnected().addCallback(self.connectToBroker) ClientService.startService(self)
def __init(self, endpoint, factory): ClientService.__init__(self, endpoint, factory, retryPolicy=backoffPolicy())
self.log.info('transport disconnected') # this is to clean up stuff. it is not our business to # possibly reconnect the underlying connection self._countdown -= 1 if self._countdown <= 0: try: reactor.stop() except ReactorNotRunning: pass if __name__ == '__main__': txaio.start_logging(level='info') # create a WAMP session object. this is reused across multiple # reconnects (if automatically reconnected) session = MyAppSession(ComponentConfig(u'realm1', {})) # create a WAMP transport factory transport = WampWebSocketClientFactory(session, url=u'ws://localhost:8080/ws') # create a connecting endpoint endpoint = TCP4ClientEndpoint(reactor, 'localhost', 8080) # create and start an automatically reconnecting client service = ClientService(endpoint, transport) service.startService() # enter the event loop reactor.run()
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
class ApplicationRunner(object): """ 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. """ log = txaio.make_logger() def __init__(self, url, realm=None, extra=None, serializers=None, ssl=None, proxy=None, headers=None, max_retries=None, initial_retry_delay=None, max_retry_delay=None, retry_delay_growth=None, retry_delay_jitter=None): """ :param url: The WebSocket URL of the WAMP router to connect to (e.g. `ws://somehost.com:8090/somepath`) :type url: str :param realm: The WAMP realm to join the application session to. :type realm: str :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 ssl: (Optional). If specified this should be an instance suitable to pass as ``sslContextFactory`` to :class:`twisted.internet.endpoints.SSL4ClientEndpoint`` such as :class:`twisted.internet.ssl.CertificateOptions`. Leaving it as ``None`` will use the result of calling Twisted's :meth:`twisted.internet.ssl.platformTrust` which tries to use your distribution's CA certificates. :type ssl: :class:`twisted.internet.ssl.CertificateOptions` :param proxy: Explicit proxy server to use; a dict with ``host`` and ``port`` keys :type proxy: dict or None :param headers: Additional headers to send (only applies to WAMP-over-WebSocket). :type headers: dict :param max_retries: Maximum number of reconnection attempts. Unlimited if set to -1. :type max_retries: int :param initial_retry_delay: Initial delay for reconnection attempt in seconds (Default: 1.0s). :type initial_retry_delay: float :param max_retry_delay: Maximum delay for reconnection attempts in seconds (Default: 60s). :type max_retry_delay: float :param retry_delay_growth: The growth factor applied to the retry delay between reconnection attempts (Default 1.5). :type retry_delay_growth: float :param retry_delay_jitter: A 0-argument callable that introduces nose into the delay. (Default random.random) :type retry_delay_jitter: float """ assert(type(url) == six.text_type) assert(realm is None or type(realm) == six.text_type) assert(extra is None or type(extra) == dict) assert(headers is None or type(headers) == dict) assert(proxy is None or type(proxy) == dict) self.url = url self.realm = realm self.extra = extra or dict() self.serializers = serializers self.ssl = ssl self.proxy = proxy self.headers = headers self.max_retries = max_retries self.initial_retry_delay = initial_retry_delay self.max_retry_delay = max_retry_delay self.retry_delay_growth = retry_delay_growth self.retry_delay_jitter = retry_delay_jitter # this if for auto-reconnection when Twisted ClientService is avail self._client_service = None # total number of successful connections self._connect_successes = 0 @public def stop(self): """ Stop reconnecting, if auto-reconnecting was enabled. """ self.log.debug('{klass}.stop()', klass=self.__class__.__name__) if self._client_service: return self._client_service.stopService() else: return succeed(None) @public 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
def makeReconnector(self, fireImmediately=True, startService=True, protocolType=Protocol, **kw): """ Create a L{ClientService} along with a L{ConnectInformation} indicating the connections in progress on its endpoint. @param fireImmediately: Should all of the endpoint connection attempts fire synchronously? @type fireImmediately: L{bool} @param startService: Should the L{ClientService} be started before being returned? @type startService: L{bool} @param protocolType: a 0-argument callable returning a new L{IProtocol} provider to be used for application-level protocol connections. @param kw: Arbitrary keyword arguments to be passed on to L{ClientService} @return: a 2-tuple of L{ConnectInformation} (for information about test state) and L{ClientService} (the system under test). The L{ConnectInformation} has 2 additional attributes; C{applicationFactory} and C{applicationProtocols}, which refer to the unwrapped protocol factory and protocol instances passed in to L{ClientService} respectively. """ nkw = {} nkw.update(clock=Clock()) nkw.update(kw) cq, endpoint = endpointForTesting(fireImmediately=fireImmediately) # `endpointForTesting` is totally generic to any LLPI client that uses # endpoints, and maintains all its state internally; however, # applicationProtocols and applicationFactory are bonus attributes that # are only specifically interesitng to tests that use wrapper # protocols. For now, set them here, externally. applicationProtocols = cq.applicationProtocols = [] class RememberingFactory(Factory, object): protocol = protocolType def buildProtocol(self, addr): result = super(RememberingFactory, self).buildProtocol(addr) applicationProtocols.append(result) return result cq.applicationFactory = factory = RememberingFactory() service = ClientService(endpoint, factory, **nkw) def stop(): service._protocol = None if service.running: service.stopService() # Ensure that we don't leave any state in the reactor after # stopService. self.assertEqual(service._clock.getDelayedCalls(), []) self.addCleanup(stop) if startService: service.startService() return cq, service
if __name__ == "__main__": config_file = sys.argv[1] if config_file == False: print("Please specify a valid config file in the arguments.") print("Example:") print("python sadface.py default.ini") irc_cfg = config['irc'] cmd_cfg = config['commands'] commands_dir_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'commands') dynamic_commands = gather_commands(commands_dir_path, cmd_cfg['dynamic_aliases'], cmd_cfg['command_configs']) triggers = '|'.join(re.escape(s) for s in config['commands']['triggers'] + config['commands']['deprecated_triggers']) dynamic_commands_regex = re.compile('\s*(' + triggers + ')\s*((' + '|'.join(dynamic_commands.keys()) + ')\s*).*') static_commands_regex = re.compile('\s*(' + triggers + ')\s*((' + '|'.join(cmd_cfg['static_commands'].keys()) + ')\s*).*') help_command_regex = re.compile('\s*(' + triggers + ')\s*(help\s*).*') markov = MarkovBrain(config['brain']['brain_file'], config['brain']['chain_length'], config['brain']['max_words']) # Lookup actual address for host (twisted only uses ipv6 if given an explicit ipv6 address) host_info = socket.getaddrinfo(irc_cfg['host'], irc_cfg['port'], 0, 0, socket.IPPROTO_TCP, socket.AI_CANONNAME)[0][4] client_string = "%s:%s:%u" % ('tls' if irc_cfg['ssl'] else 'tcp', host_info[0].replace(':', '\:'), host_info[1]) endpoint = clientFromString(reactor, client_string) bot_client_service = ClientService(endpoint, sadfaceBotFactory(config, markov, dynamic_commands, dynamic_commands_regex, static_commands_regex, help_command_regex)) bot_client_service.startService() reactor.run() markov.close()
class ZenHubClient(object): """A client for connecting to ZenHub as a ZenHub Worker. After start is called, this class automatically handles connecting to ZenHub, registering the zenhubworker with ZenHub, and automatically reconnecting to ZenHub if the connection to ZenHub is corrupted for any reason. """ def __init__(self, reactor, endpoint, credentials, worker, timeout): """Initialize a ZenHubClient instance. @param reactor {IReactorCore} @param endpoint {IStreamClientEndpoint} Where zenhub is found @param credentials {IUsernamePassword} Credentials to log into ZenHub. @param worker {IReferenceable} Reference to worker @param timeout {float} Seconds to wait before determining whether ZenHub is unresponsive. """ self.__reactor = reactor self.__endpoint = endpoint self.__credentials = credentials self.__worker = worker self.__timeout = timeout self.__stopping = False self.__pinger = None self.__service = None self.__log = getLogger(self) self.__signalFile = ConnectedToZenHubSignalFile() def start(self): """Start connecting to ZenHub.""" self.__stopping = False factory = ZenPBClientFactory() self.__service = ClientService( self.__endpoint, factory, retryPolicy=backoffPolicy(initialDelay=0.5, factor=3.0), ) self.__service.startService() self.__prepForConnection() def stop(self): """Stop connecting to ZenHub.""" self.__stopping = True self.__reset() def restart(self): """Restart the connect to ZenHub.""" self.__reset() self.start() def __reset(self): if self.__pinger: self.__pinger.stop() self.__pinger = None if self.__service: self.__service.stopService() self.__service = None self.__signalFile.remove() def __prepForConnection(self): if not self.__stopping: self.__log.info("Prepping for connection") self.__service.whenConnected().addCallbacks( self.__connected, self.__notConnected, ) def __disconnected(self, *args): # Called when the connection to ZenHub is lost. # Ensures that processing resumes when the connection to ZenHub # is restored. self.__log.info( "Lost connection to ZenHub: %s", args[0] if args else "<no reason given>", ) if self.__pinger: self.__pinger.stop() self.__pinger = None self.__signalFile.remove() self.__prepForConnection() def __notConnected(self, *args): self.__log.info("Not connected! %r", args) @defer.inlineCallbacks def __connected(self, broker): # Called when a connection to ZenHub is established. # Logs into ZenHub and passes up a worker reference for ZenHub # to use to dispatch method calls. # Sometimes broker.transport doesn't have a 'socket' attribute if not hasattr(broker.transport, "socket"): self.restart() defer.returnValue(None) self.__log.info("Connection to ZenHub established") try: setKeepAlive(broker.transport.socket) zenhub = yield self.__login(broker) yield zenhub.callRemote( "reportingForWork", self.__worker, workerId=self.__worker.instanceId, ) ping = PingZenHub(zenhub, self) self.__pinger = task.LoopingCall(ping) d = self.__pinger.start(self.__timeout, now=False) d.addErrback(self.__pingFail) # Catch and pass on errors except defer.CancelledError: self.__log.error("Timed out trying to login to ZenHub") self.restart() defer.returnValue(None) except Exception as ex: self.__log.error( "Unable to report for work: (%s) %s", type(ex), ex, ) self.__signalFile.remove() self.__reactor.stop() else: self.__log.info("Logged into ZenHub") self.__signalFile.touch() # Connection complete; install a listener to be notified if # the connection is lost. broker.notifyOnDisconnect(self.__disconnected) def __login(self, broker): d = broker.factory.login(self.__credentials, self.__worker) timeoutCall = self.__reactor.callLater(self.__timeout, d.cancel) def completedLogin(arg): if timeoutCall.active(): timeoutCall.cancel() return arg d.addBoth(completedLogin) return d def __pingFail(self, ex): self.__log.error("Pinger failed: %s", ex)
def startService(self): log.info("starting MQTT Client Subscriber Service") # invoke whenConnected() inherited method self.whenConnected().addCallback(self.connectToBroker) ClientService.startService(self)
class NotificationSourceIntegrationTest(IntegrationTest): @inlineCallbacks def setUp(self): super(NotificationSourceIntegrationTest, self).setUp() self.endpoint = AMQEndpoint( reactor, self.rabbit.config.hostname, self.rabbit.config.port, username="******", password="******", heartbeat=1) self.policy = backoffPolicy(initialDelay=0) self.factory = AMQFactory(spec=AMQP0_8_SPEC_PATH) self.service = ClientService( self.endpoint, self.factory, retryPolicy=self.policy) self.connector = NotificationConnector(self.service) self.source = NotificationSource(self.connector) self.client = yield self.endpoint.connect(self.factory) self.channel = yield self.client.channel(1) yield self.channel.channel_open() yield self.channel.queue_declare(queue="uuid") self.service.startService() @inlineCallbacks def tearDown(self): self.service.stopService() super(NotificationSourceIntegrationTest, self).tearDown() # Wrap resetting queues and client in a try/except, since the broker # may have been stopped (e.g. when this is the last test being run). try: yield self.channel.queue_delete(queue="uuid") except: pass finally: yield self.client.close() @inlineCallbacks def test_get_after_publish(self): """ Calling get() after a message has been published in the associated queue returns a Notification for that message. """ yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) notification = yield self.source.get("uuid", 0) self.assertEqual("hello", notification.payload) @inlineCallbacks def test_get_before_publish(self): """ Calling get() before a message has been published in the associated queue will wait until publication. """ deferred = self.source.get("uuid", 0) self.assertFalse(deferred.called) yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) notification = yield deferred self.assertEqual("hello", notification.payload) @inlineCallbacks def test_get_with_error(self): """ If an error occurs in during get(), the client is closed so we can query messages again. """ yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) with self.assertRaises(NotFound): yield self.source.get("uuid-unknown", 0) notification = yield self.source.get("uuid", 0) self.assertEqual("hello", notification.payload) @inlineCallbacks def test_get_concurrent_with_error(self): """ If an error occurs in a call to get(), other calls don't fail, and are retried on reconnection instead. """ client1 = yield self.service.whenConnected() deferred = self.source.get("uuid", 0) with self.assertRaises(NotFound): yield self.source.get("uuid-unknown", 0) yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) notification = yield deferred self.assertEqual("hello", notification.payload) client2 = yield self.service.whenConnected() # The ClientService has reconnected, yielding a new client. self.assertIsNot(client1, client2) @inlineCallbacks def test_get_timeout(self): """ Calls to get() timeout after a certain amount of time if no message arrived on the queue. """ self.source.timeout = 1 with self.assertRaises(Timeout): yield self.source.get("uuid", 0) client = yield self.service.whenConnected() channel = yield client.channel(1) # The channel is still opened self.assertFalse(channel.closed) # The consumer has been deleted self.assertNotIn("uuid.0", client.queues) @inlineCallbacks def test_get_with_broker_shutdown_during_consume(self): """ If rabbitmq gets shutdown during the basic-consume call, we wait for the reconection and retry transparently. """ # This will make the connector setup the channel before we call # get(), so by the time we call it in the next line all # connector-related deferreds will fire synchronously and the # code will block on basic-consume. yield self.connector() d = self.source.get("uuid", 0) # Restart rabbitmq yield self.client.close() yield self.client.disconnected.wait() self.rabbit.cleanUp() self.rabbit.config = RabbitServerResources( port=self.rabbit.config.port) # Ensure that we use the same port self.rabbit.setUp() # Get a new channel and re-declare the queue, since the restart has # destroyed it. self.client = yield self.endpoint.connect(self.factory) self.channel = yield self.client.channel(1) yield self.channel.channel_open() yield self.channel.queue_declare(queue="uuid") # Publish a message in the queue yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) notification = yield d self.assertEqual("hello", notification.payload) @inlineCallbacks def test_get_with_broker_die_during_consume(self): """ If rabbitmq dies during the basic-consume call, we wait for the reconection and retry transparently. """ # This will make the connector setup the channel before we call # get(), so by the time we call it in the next line all # connector-related deferreds will fire synchronously and the # code will block on basic-consume. yield self.connector() d = self.source.get("uuid", 0) # Kill rabbitmq and start it again yield self.client.close() yield self.client.disconnected.wait() self.rabbit.runner.kill() self.rabbit.cleanUp() self.rabbit.config = RabbitServerResources( port=self.rabbit.config.port) # Ensure that we use the same port self.rabbit.setUp() # Get a new channel and re-declare the queue, since the crash has # destroyed it. self.client = yield self.endpoint.connect(self.factory) self.channel = yield self.client.channel(1) yield self.channel.channel_open() yield self.channel.queue_declare(queue="uuid") # Publish a message in the queue yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) notification = yield d self.assertEqual("hello", notification.payload) @inlineCallbacks def test_wb_get_with_broker_shutdown_during_message_wait(self): """ If rabbitmq gets shutdown while we wait for messages, we transparently wait for the reconnection and try again. """ # This will make the connector setup the channel before we call # get(), so by the time we call it in the next line all # connector-related deferreds will fire synchronously and the # code will block on basic-consume. yield self.connector() d = self.source.get("uuid", 0) # Acquiring the channel lock makes sure that basic-consume has # succeeded and we started waiting for the message. yield self.source._channel_lock.acquire() self.source._channel_lock.release() # Restart rabbitmq yield self.client.close() yield self.client.disconnected.wait() self.rabbit.cleanUp() self.rabbit.config = RabbitServerResources( port=self.rabbit.config.port) # Ensure that we use the same port self.rabbit.setUp() # Get a new channel and re-declare the queue, since the restart has # destroyed it. self.client = yield self.endpoint.connect(self.factory) self.channel = yield self.client.channel(1) yield self.channel.channel_open() yield self.channel.queue_declare(queue="uuid") # Publish a message in the queue yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) notification = yield d self.assertEqual("hello", notification.payload) @inlineCallbacks def test_wb_heartbeat(self): """ If heartbeat checks fail due to network issues, we keep re-trying until the network recovers. """ self.service.stopService() # Put a TCP proxy between NotificationSource and RabbitMQ, to simulate # packets getting dropped on the floor. proxy = ProxyService( self.rabbit.config.hostname, self.rabbit.config.port) proxy.startService() self.addCleanup(proxy.stopService) self.endpoint._port = proxy.port self.service = ClientService( self.endpoint, self.factory, retryPolicy=self.policy) self.connector._service = self.service self.service.startService() # This will make the connector setup the channel before we call # get(), so by the time we call it in the next line all # connector-related deferreds will fire synchronously and the # code will block on basic-consume. channel = yield self.connector() deferred = self.source.get("uuid", 0) # Start dropping packets on the floor proxy.block() # Publish a notification, which won't be delivered just yet. yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) # Wait for the first connection to terminate, because heartbeat # checks will fail. yield channel.client.disconnected.wait() # Now let packets flow again. proxy.unblock() # The situation got recovered. notification = yield deferred self.assertEqual("hello", notification.payload) self.assertEqual(2, proxy.connections) @inlineCallbacks def test_reject_notification(self): """ Calling reject() on a Notification puts the associated message back in the queue so that it's available to subsequent get() calls. """ yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) notification = yield self.source.get("uuid", 0) yield notification.reject() notification = yield self.source.get("uuid", 1) self.assertEqual("hello", notification.payload) @inlineCallbacks def test_ack_message(self): """ Calling ack() on a Notification confirms the removal of the associated message from the queue, making subsequent calls waiting for another message. """ yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) notification = yield self.source.get("uuid", 0) yield notification.ack() yield self.channel.basic_publish( routing_key="uuid", content=Content("hello 2")) notification = yield self.source.get("uuid", 1) self.assertEqual("hello 2", notification.payload) @inlineCallbacks def test_ack_with_broker_shutdown(self): """ If rabbitmq gets shutdown before we ack a Notification, an error is raised. """ client = yield self.service.whenConnected() yield self.channel.basic_publish( routing_key="uuid", content=Content("hello")) notification = yield self.source.get("uuid", 0) self.rabbit.cleanUp() yield client.disconnected.wait() try: yield notification.ack() except Bounced: pass else: self.fail("Notification not bounced") self.rabbit.config = RabbitServerResources( port=self.rabbit.config.port) # Ensure that we use the same port self.rabbit.setUp()