Exemplo n.º 1
0
    def test_independent(self):
        """Test that waiters are independent."""
        result = []

        def bad(res):
            result.append(res)
            raise Exception()

        def fired(res):
            result.append(res)

        e = TwistedEvent()

        d = e.wait().addCallback(bad)
        e.wait().addCallback(fired)
        d.addErrback(lambda _ : None)

        e.fire()

        self.assertEqual(result, [True, True])
Exemplo n.º 2
0
    def test_fire(self):
        """Test event success."""
        result = []

        def fired(res):
            result.append(res)

        e = TwistedEvent()

        e.wait().addCallback(fired)
        self.assertEqual(result, [])

        e.fire()
        self.assertEqual(result, [True])

        self.assertRaises(AlreadyFiredError, e.fire)
        self.assertRaises(AlreadyFiredError, e.fail, None)

        e.wait().addCallback(fired)
        self.assertEqual(result, [True, True])

        e.fail_if_not_fired(None)
Exemplo n.º 3
0
class AMQClient(FrameReceiver):
    channelClass = AMQChannel

    # Max unreceived heartbeat frames. The AMQP standard says it's 3.
    MAX_UNSEEN_HEARTBEAT = 3

    def __init__(self,
                 delegate,
                 vhost,
                 spec,
                 heartbeat=0,
                 clock=None,
                 insist=False):
        FrameReceiver.__init__(self, spec)
        self.delegate = delegate

        # XXX Cyclic dependency
        self.delegate.client = self

        self.vhost = vhost

        self.channelFactory = type("Channel%s" % self.spec.klass.__name__,
                                   (self.channelClass, self.spec.klass), {})
        self.channels = {}
        self.channelLock = defer.DeferredLock()

        self.outgoing = defer.DeferredQueue()
        self.work = defer.DeferredQueue()

        self.started = TwistedEvent()
        self.disconnected = TwistedEvent()  # Fired upon connection shutdown
        self.closed = False

        self.queueLock = defer.DeferredLock()
        self.basic_return_queue = TimeoutDeferredQueue()

        self.queues = {}

        self.outgoing.get().addCallback(self.writer)
        self.work.get().addCallback(self.worker)
        self.heartbeatInterval = heartbeat
        self.insist = insist
        if clock is None:
            from twisted.internet import reactor
            clock = reactor
        self.clock = clock
        if self.heartbeatInterval > 0:
            self.checkHB = self.clock.callLater(
                self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT,
                self.check_heartbeat)
            self.sendHB = LoopingCall(self.send_heartbeat)
            self.sendHB.clock = self.clock
            d = self.started.wait()
            d.addCallback(lambda _: self.reschedule_send_heartbeat())
            d.addCallback(lambda _: self.reschedule_check_heartbeat())
            # If self.started fails, don't start the heartbeat.
            d.addErrback(lambda _: None)

    def reschedule_send_heartbeat(self):
        if self.heartbeatInterval > 0:
            if self.sendHB.running:
                self.sendHB.stop()
            self.sendHB.start(self.heartbeatInterval, now=False)

    def reschedule_check_heartbeat(self):
        if self.checkHB.active():
            self.checkHB.cancel()
        self.checkHB = self.clock.callLater(
            self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT,
            self.check_heartbeat)

    def check_0_8(self):
        return (self.spec.minor, self.spec.major) == (0, 8)

    @defer.inlineCallbacks
    def channel(self, id):
        yield self.channelLock.acquire()
        try:
            try:
                ch = self.channels[id]
            except KeyError:
                ch = self.channelFactory(id, self.outgoing, self)
                self.channels[id] = ch
        finally:
            self.channelLock.release()
        defer.returnValue(ch)

    @defer.inlineCallbacks
    def queue(self, key):
        yield self.queueLock.acquire()
        try:
            try:
                q = self.queues[key]
            except KeyError:
                q = TimeoutDeferredQueue(clock=self.clock)
                self.queues[key] = q
        finally:
            self.queueLock.release()
        defer.returnValue(q)

    @defer.inlineCallbacks
    def close(self, reason=None, within=0):
        """Explicitely close the connection.

        @param reason: Optional closing reason. If not given, ConnectionDone
            will be used.
        @param within: Shutdown the client within this amount of seconds. If
            zero (the default), all channels and queues will be closed
            immediately. If greater than 0, try to close the AMQP connection
            cleanly, by sending a "close" method and waiting for "close-ok". If
            no reply is received within the given amount of seconds, the
            transport will be forcely shutdown.
        """
        if self.closed:
            return

        if reason is None:
            reason = ConnectionDone()

        if within > 0:
            channel0 = yield self.channel(0)
            deferred = channel0.connection_close()
            call = self.clock.callLater(within, deferred.cancel)
            try:
                yield deferred
            except defer.CancelledError:
                pass
            else:
                call.cancel()

        self.do_close(reason)

    def do_close(self, reason):
        """Called when connection_close() is received"""
        # Let's close all channels and queues, since we don't want to write
        # any more data and no further read will happen.
        for ch in self.channels.values():
            ch.close(reason)
        for q in self.queues.values():
            q.close()
        self.delegate.close(reason)

    def writer(self, frame):
        self.send_frame(frame)
        self.outgoing.get().addCallback(self.writer)

    def worker(self, queue):
        d = self.dispatch(queue)

        def cb(ign):
            self.work.get().addCallback(self.worker)

        d.addCallback(cb)
        d.addErrback(self.close)

    @defer.inlineCallbacks
    def dispatch(self, queue):
        frame = yield queue.get()
        channel = yield self.channel(frame.channel)
        payload = frame.payload
        if payload.method.content:
            content = yield read_content(queue)
        else:
            content = None
        # Let the caller deal with exceptions thrown here.
        message = Message(payload.method, payload.args, content)
        self.delegate.dispatch(channel, message)

    # As soon as we connect to the target AMQP broker, send the init string
    def connectionMade(self):
        self.send_init_string()
        self.set_frame_mode()

    def frame_received(self, frame):
        self.process_frame(frame)

    def send_frame(self, frame):
        if frame.payload.type != Frame.HEARTBEAT:
            self.reschedule_send_heartbeat()
        FrameReceiver.send_frame(self, frame)

    @defer.inlineCallbacks
    def process_frame(self, frame):
        ch = yield self.channel(frame.channel)
        if frame.payload.type == Frame.HEARTBEAT:
            self.last_heartbeat_received = time()
        else:
            ch.dispatch(frame, self.work)
        if self.heartbeatInterval > 0 and not self.closed:
            self.reschedule_check_heartbeat()

    @defer.inlineCallbacks
    def authenticate(self,
                     username,
                     password,
                     mechanism='AMQPLAIN',
                     locale='en_US'):
        if mechanism == 'AMQPLAIN':
            response = {"LOGIN": username, "PASSWORD": password}
        elif mechanism == 'PLAIN':
            response = "\0" + username + "\0" + password
        else:
            raise ValueError('Unknown mechanism:' + mechanism)

        yield self.start(response, mechanism, locale)

    @defer.inlineCallbacks
    def start(self, response, mechanism='AMQPLAIN', locale='en_US'):
        self.response = response
        self.mechanism = mechanism
        self.locale = locale

        yield self.started.wait()

        channel0 = yield self.channel(0)
        if self.check_0_8():
            result = yield channel0.connection_open(self.vhost,
                                                    insist=self.insist)
        else:
            result = yield channel0.connection_open(self.vhost)
        defer.returnValue(result)

    def send_heartbeat(self):
        self.send_frame(Frame(0, Heartbeat()))
        self.last_heartbeat_sent = time()

    def check_heartbeat(self):
        if self.checkHB.active():
            self.checkHB.cancel()
        # Abort the connection, since the other pear is unresponsive and
        # there's no point in shutting down cleanly and trying to flush
        # pending data.
        self.transport.abortConnection()

    def connectionLost(self, reason):
        if self.heartbeatInterval > 0:
            if self.sendHB.running:
                self.sendHB.stop()
            if self.checkHB.active():
                self.checkHB.cancel()
        self.close(reason)
        self.disconnected.fire()

    def channel_failed(self, channel, reason):
        """Unexpected channel close"""
        pass