Beispiel #1
0
 def synchronous(self):
     '''
     True if transport is synchronous or the connection has been forced
     into synchronous mode, False otherwise.
     '''
     if self._transport is None:
         if self._close_info and len(self._close_info['reply_text']) > 0:
             raise ConnectionClosed("connection is closed: %s : %s" %
                                    (self._close_info['reply_code'],
                                     self._close_info['reply_text']))
         raise ConnectionClosed("connection is closed")
     return self.transport.synchronous or self._synchronous
Beispiel #2
0
    def read_frames(self):
        '''
        Read frames from the transport and process them. Some transports may
        choose to do this in the background, in several threads, and so on.
        '''
        # It's possible in a concurrent environment that our transport handle
        # has gone away, so handle that cleanly.
        # TODO: Consider moving this block into Translator base class. In many
        # ways it belongs there. One of the problems though is that this is
        # essentially the read loop. Each Transport has different rules for
        # how to kick this off, and in the case of gevent, this is how a
        # blocking call to read from the socket is kicked off.
        if self._transport is None:
            return

        # Send a heartbeat (if needed)
        self._channels[0].send_heartbeat()

        data = self._transport.read(self._heartbeat)
        if data is None:
            return

        reader = Reader(data)
        p_channels = set()

        try:
            for frame in Frame.read_frames(reader):
                if self._debug > 1:
                    self.logger.debug("READ: %s", frame)
                self._frames_read += 1
                ch = self.channel(frame.channel_id)
                ch.buffer_frame(frame)
                p_channels.add(ch)
        except Frame.FrameError as e:
            # Frame error in the peer, disconnect
            self.close(reply_code=501,
                       reply_text='frame error from %s : %s' %
                       (self._host, str(e)),
                       class_id=0,
                       method_id=0,
                       disconnect=True)
            raise ConnectionClosed("connection is closed: %s : %s" %
                                   (self._close_info['reply_code'],
                                    self._close_info['reply_text']))

        self._transport.process_channels(p_channels)

        # HACK: read the buffer contents and re-buffer.  Would prefer to pass
        # buffer back, but there's no good way of asking the total size of the
        # buffer, comparing to tell(), and then re-buffering.  There's also no
        # ability to clear the buffer up to the current position. It would be
        # awesome if we could free that memory without a new allocation.
        if reader.tell() < len(data):
            self._transport.buffer(data[reader.tell():])
Beispiel #3
0
    def send_frame(self, frame):
        '''
        Send a single frame. If there is no transport or we're not connected
        yet, append to the output buffer, else send immediately to the socket.
        This is called from within the MethodFrames.
        '''
        if self._closed:
            if self._close_info and len(self._close_info['reply_text']) > 0:
                raise ConnectionClosed("connection is closed: %s : %s" %
                                       (self._close_info['reply_code'],
                                        self._close_info['reply_text']))
            raise ConnectionClosed("connection is closed")

        if self._transport is None or \
                (not self._connected and frame.channel_id != 0):
            self._output_frame_buffer.append(frame)
            return

        if self._debug > 1:
            self.logger.debug("WRITE: %s", frame)

        buf = bytearray()
        frame.write_frame(buf)
        if len(buf) > self._frame_max:
            self.close(
                reply_code=501,
                reply_text='attempted to send frame of %d bytes, frame max %d'
                % (len(buf), self._frame_max),
                class_id=0,
                method_id=0,
                disconnect=True)
            raise ConnectionClosed("connection is closed: %s : %s" %
                                   (self._close_info['reply_code'],
                                    self._close_info['reply_text']))
        self._transport.write(buf)

        self._frames_written += 1
Beispiel #4
0
    def read_frames(self):
        '''
        Read frames from the transport and process them. Some transports may
        choose to do this in the background, in several threads, and so on.
        '''
        # It's possible in a concurrent environment that our transport handle
        # has gone away, so handle that cleanly.
        # TODO: Consider moving this block into Translator base class. In many
        # ways it belongs there. One of the problems though is that this is
        # essentially the read loop. Each Transport has different rules for
        # how to kick this off, and in the case of gevent, this is how a
        # blocking call to read from the socket is kicked off.
        if self._transport is None:
            return

        # Send a heartbeat (if needed)
        self._channels[0].send_heartbeat()

        data = self._transport.read(self._heartbeat)
        current_time = time.time()

        if data is None:
            # Wait for 2 heartbeat intervals before giving up. See AMQP 4.2.7:
            # "If a peer detects no incoming traffic (i.e. received octets) for two heartbeat intervals or longer,
            # it should close the connection"
            if self._heartbeat and (current_time-self._last_octet_time > 2*self._heartbeat):
                msg = 'Heartbeats not received from %s for %d seconds' % (self._host, 2*self._heartbeat)
                self.transport_closed(msg=msg)
                raise ConnectionClosed('Connection is closed: ' + msg)
            return
        self._last_octet_time = current_time
        reader = Reader(data)
        p_channels = set()

        try:
            for frame in Frame.read_frames(reader):
                if self._debug > 1:
                    self.logger.debug("READ: %s", frame)
                self._frames_read += 1
                ch = self.channel(frame.channel_id)
                ch.buffer_frame(frame)
                p_channels.add(ch)
        except Frame.FrameError as e:
            # Frame error in the peer, disconnect
            self.close(reply_code=501,
                       reply_text='frame error from %s : %s' % (
                           self._host, str(e)),
                       class_id=0, method_id=0, disconnect=True)
            raise ConnectionClosed("connection is closed: %s : %s" %
                                   (self._close_info['reply_code'],
                                    self._close_info['reply_text']))

        # NOTE: we process channels after buffering unused data in order to
        # preserve the integrity of the input stream in case a channel needs to
        # read input, such as when a channel framing error necessitates the use
        # of the synchronous channel.close method. See `Channel.process_frames`.
        #
        # HACK: read the buffer contents and re-buffer.  Would prefer to pass
        # buffer back, but there's no good way of asking the total size of the
        # buffer, comparing to tell(), and then re-buffering.  There's also no
        # ability to clear the buffer up to the current position. It would be
        # awesome if we could free that memory without a new allocation.
        if reader.tell() < len(data):
            self._transport.buffer(data[reader.tell():])

        self._transport.process_channels(p_channels)