def test_read_frame_on_full_frame(self): class FrameReader(Frame): @classmethod def type(self): return 45 @classmethod def parse(self, channel_id, payload): return 'no_frame' FrameReader.register() self.mock(frame, 'Reader') reader = self.mock() payload = self.mock() expect(reader.read_octet).returns(45) # frame type expect(reader.read_short).returns(32) # channel id expect(reader.read_long).returns(42) # size expect(reader.tell).returns(5) expect(frame.Reader).args(reader, 5, 42).returns(payload) expect(reader.seek).args(42, 1) expect(reader.read_octet).returns(0xce) expect(FrameReader.parse).args(32, payload).returns('a_frame') assertEquals('a_frame', Frame._read_frame(reader))
def dispatch(self, frame): ''' Override the default dispatch since we don't need the rest of the stack. ''' if frame.type() == HeartbeatFrame.type(): self.send_heartbeat() elif frame.type() == MethodFrame.type(): if frame.class_id == 10: cb = self._method_map.get(frame.method_id) if cb: method = self.clear_synchronous_cb(cb) method(frame) else: raise Channel.InvalidMethod( "unsupported method %d on channel %d", frame.method_id, self.channel_id) else: raise Channel.InvalidClass( "class %d is not supported on channel %d", frame.class_id, self.channel_id) else: raise Frame.InvalidFrameType( "frame type %d is not supported on channel %d", frame.type(), self.channel_id)
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():])
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():])
def test_read_frames_reads_until_buffer_underflow(self): reader = mock() expect(reader.tell).returns(0) expect(Frame._read_frame).args(reader).returns('frame1') expect(reader.tell).returns(2) expect(Frame._read_frame).args(reader).returns('frame2') expect(reader.tell).returns(3) expect(Frame._read_frame).args(reader).raises(Reader.BufferUnderflow) expect(reader.seek).args(3) assertEquals(deque(['frame1', 'frame2']), Frame.read_frames(reader))
def __init__(self, *args, **kwargs): Frame.__init__(self, *args, **kwargs)
def __init__(self, channel_id, class_id, method_id, args=None): Frame.__init__(self, channel_id) self._class_id = class_id self._method_id = method_id self._args = args
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)
def __init__(self, channel_id, class_id, weight, size, properties={}): Frame.__init__(self, channel_id) self._class_id = class_id self._weight = weight self._size = size self._properties = properties
def test_str(self): frame = Frame(42) assert_equals('Frame[channel: 42]', str(frame))
def test_properties(self): frame = Frame('channel_id') assert_equals('channel_id', frame.channel_id)
def __init__(self, channel_id, payload): Frame.__init__(self, channel_id) self._payload = payload
def test_repr(self): expect(Frame.__str__).returns('foo') frame = Frame(42) assert_equals('foo', repr(frame))
def test_write_frame(self): frame = Frame(42) assert_raises(NotImplementedError, frame.write_frame, 'stream')