def test_field_decimal(self): w = Writer() w._field_decimal(Decimal('1.50')) assert_equals('D\x02\x00\x00\x00\x96', w._output_buffer) w = Writer() w._field_decimal(Decimal('-1.50')) assert_equals('D\x02\xff\xff\xff\x6a', w._output_buffer)
def test_write_bits(self): w = Writer() assert_true(w is w.write_bits(False, True)) assert_equals(bytearray('\x02'), w._output_buffer) w = Writer() assert_true(w is w.write_bits(False, False, True, True, True)) assert_equals(bytearray('\x1c'), w._output_buffer) assert_raises(ValueError, w.write_bits, *((True, ) * 9))
def test_write_shortstr(self): w = Writer() assert_true(w is w.write_shortstr('')) assert_equals(bytearray('\x00'), w._output_buffer) w = Writer() assert_true(w is w.write_shortstr('a' * 255)) assert_equals(bytearray('\xff' + ('a' * 255)), w._output_buffer) w = Writer() assert_true(w is w.write_shortstr('Au\xc3\x9ferdem'.decode('utf8'))) assert_equals(bytearray('\x09Au\xc3\x9ferdem'), w._output_buffer) assert_raises(ValueError, w.write_shortstr, 'a' * 256)
def test_parse_slow_for_standard_properties(self): HeaderFrame.DEFAULT_PROPERTIES = False bit_writer = Writer() val_writer = Writer() # strip ms because amqp doesn't include it now = datetime.utcfromtimestamp( long(time.mktime(datetime.now().timetuple()))) bit_field = 0 for pname, ptype, reader, writer, mask in HeaderFrame.PROPERTIES: bit_field = (bit_field << 1) | 1 if ptype == 'shortstr': val_writer.write_shortstr(pname) elif ptype == 'octet': val_writer.write_octet(42) elif ptype == 'timestamp': val_writer.write_timestamp(now) elif ptype == 'table': val_writer.write_table({'foo': 'bar'}) bit_field <<= (16 - len(HeaderFrame.PROPERTIES)) bit_writer.write_short(bit_field) header_writer = Writer() header_writer.write_short(5) header_writer.write_short(6) header_writer.write_longlong(7) payload = header_writer.buffer() payload += bit_writer.buffer() payload += val_writer.buffer() reader = Reader(payload) frame = HeaderFrame.parse(4, reader) HeaderFrame.DEFAULT_PROPERTIES = True for pname, ptype, reader, writer, mask in HeaderFrame.PROPERTIES: if ptype == 'shortstr': self.assertEquals(pname, frame.properties[pname]) elif ptype == 'octet': self.assertEquals(42, frame.properties[pname]) elif ptype == 'timestamp': self.assertEquals(now, frame.properties[pname]) elif ptype == 'table': self.assertEquals({'foo': 'bar'}, frame.properties[pname]) assert_equals(4, frame.channel_id) assert_equals(5, frame._class_id) assert_equals(6, frame._weight) assert_equals(7, frame._size)
def test_write_longstr(self): # We can't actually build a string 2**32 long, so can't also test the # valueerror without mocking w = Writer() assert_true(w is w.write_longstr('')) assert_equals(bytearray('\x00\x00\x00\x00'), w._output_buffer) w = Writer() assert_true(w is w.write_longstr('a' * (2**16))) assert_equals(bytearray('\x00\x01\x00\x00' + ('a' * 2**16)), w._output_buffer) w = Writer() assert_true(w is w.write_longstr('Au\xc3\x9ferdem'.decode('utf8'))) assert_equals(bytearray('\x00\x00\x00\x09Au\xc3\x9ferdem'), w._output_buffer)
def bind(self, queue, exchange, routing_key='', nowait=True, arguments={}, ticket=None, cb=None): ''' bind to a queue. ''' nowait = nowait and self.allow_nowait() and not cb args = Writer() args.write_short(ticket or self.default_ticket).\ write_shortstr(queue).\ write_shortstr(exchange).\ write_shortstr(routing_key).\ write_bit(nowait).\ write_table(arguments) self.send_frame(MethodFrame(self.channel_id, 50, 20, args)) if not nowait: self._bind_cb.append(cb) self.channel.add_synchronous_cb(self._recv_bind_ok)
def declare(self, queue='', passive=False, durable=False, exclusive=False, auto_delete=True, nowait=True, arguments={}, ticket=None, cb=None): ''' Declare a queue. By default is asynchronoous but will be synchronous if nowait=False or a callback is defined. In synchronous mode, returns (message_count, consumer_count) queue - The name of the queue cb - An optional method which will be called with (queue_name, msg_count, consumer_count) if nowait=False ''' nowait = nowait and self.allow_nowait() and not cb args = Writer() args.write_short(ticket or self.default_ticket).\ write_shortstr(queue).\ write_bits(passive, durable, exclusive, auto_delete, nowait).\ write_table(arguments) self.send_frame(MethodFrame(self.channel_id, 50, 10, args)) if not nowait: self._declare_cb.append(cb) return self.channel.add_synchronous_cb(self._recv_declare_ok)
def cancel(self, consumer_tag='', nowait=True, consumer=None, cb=None): ''' Cancel a consumer. Can choose to delete based on a consumer tag or the function which is consuming. If deleting by function, take care to only use a consumer once per channel. ''' if consumer: for (tag,func) in self._consumer_cb.iteritems(): if func==consumer: consumer_tag = tag break nowait = nowait and self.allow_nowait() and not cb args = Writer() args.write_shortstr(consumer_tag).\ write_bit(nowait) self.send_frame( MethodFrame(self.channel_id, 60, 30, args) ) if not nowait: self._cancel_cb.append( cb ) self.channel.add_synchronous_cb( self._recv_cancel_ok ) else: try: del self._consumer_cb[consumer_tag] except KeyError: self.logger.warning( 'no callback registered for consumer tag " %s "', consumer_tag )
def test_write_field_supports_subclasses(self): class SubString(str): pass w = Writer() w._write_field(SubString('foo')) assert_equals('S\x00\x00\x00\x03foo', w._output_buffer)
def close(self, reply_code=0, reply_text='', class_id=0, method_id=0): ''' Close this channel. Caller has the option of specifying the reason for closure and the class and method ids of the current frame in which an error occurred. If in the event of an exception, the channel will be marked as immediately closed. If channel is already closed, call is ignored. ''' if not getattr(self, 'channel', None) or self.channel._closed: return self.channel._close_info = { 'reply_code': reply_code, 'reply_text': reply_text, 'class_id': class_id, 'method_id': method_id } # exceptions here likely due to race condition as connection is closing # cap the reply_text we send because it may be arbitrarily long try: args = Writer() args.write_short(reply_code) args.write_shortstr(reply_text[:255]) args.write_short(class_id) args.write_short(method_id) self.send_frame(MethodFrame(self.channel_id, 20, 40, args)) self.channel.add_synchronous_cb(self._recv_close_ok) finally: # Immediately set the closed flag so that no more frames can be sent # NOTE: in synchronous mode, by the time this is called we will have # already run self.channel._closed_cb and so the channel reference is # gone. if self.channel: self.channel._closed = True
def write_frame(self, buf): writer = Writer(buf) writer.write_octet(self.type()) writer.write_short(self.channel_id) # Write a temporary value for the total length of the frame stream_args_len_pos = len(buf) writer.write_long(0) # Mark the point in the stream where we start writing arguments, # *including* the class and method ids. stream_method_pos = len(buf) writer.write_short(self.class_id) writer.write_short(self.method_id) # This is assuming that args is a Writer if self._args is not None: writer.write(self._args.buffer()) # Write the total length back at the position we allocated stream_len = len(buf) - stream_method_pos writer.write_long_at(stream_len, stream_args_len_pos) # Write the footer writer.write_octet(0xce)
def bind(self, queue, exchange, routing_key='', nowait=True, arguments={}, ticket=None, cb=None): ''' bind to a queue. ''' # If a callback is defined, then we have to use synchronous transactions. if cb: nowait = False args = Writer() args.write_short(ticket or self.default_ticket).\ write_shortstr(queue).\ write_shortstr(exchange).\ write_shortstr(routing_key).\ write_bit(nowait).\ write_table(arguments) self.send_frame(MethodFrame(self.channel_id, 50, 20, args)) if not nowait: self.channel.add_synchronous_cb(self._recv_bind_ok) self._bind_cb.append(cb)
def close(self, reply_code=0, reply_text='', class_id=0, method_id=0): ''' Close this channel. Caller has the option of specifying the reason for closure and the class and method ids of the current frame in which an error occurred. If in the event of an exception, the channel will be marked as immediately closed. If channel is already closed, call is ignored. ''' if self._closed: return self._close_info = { 'reply_code' : reply_code, 'reply_text' : reply_text, 'class_id' : class_id, 'method_id' : method_id } # exception likely due to race condition as connection is closing try: args = Writer() args.write_short( reply_code ) args.write_shortstr( reply_text ) args.write_short( class_id ) args.write_short( method_id ) self.send_frame( MethodFrame(self.channel_id, 20, 40, args) ) self.channel.add_synchronous_cb( self._recv_close_ok ) except: self.logger.error("Failed to close channel %d", self.channel_id, exc_info=True) # Immediately set the closed flag so that no more frames can be sent self._closed = True
def consume(self, queue, consumer, consumer_tag='', no_local=False, no_ack=True, exclusive=False, nowait=True, ticket=None, cb=None): ''' Start a queue consumer. If `cb` is supplied, will be called when broker confirms that consumer is registered. ''' nowait = nowait and self.allow_nowait() and not cb if nowait and consumer_tag == '': consumer_tag = self._generate_consumer_tag() args = Writer() args.write_short(ticket or self.default_ticket).\ write_shortstr(queue).\ write_shortstr(consumer_tag).\ write_bits( no_local, no_ack, exclusive, nowait ).\ write_table({}) # unused according to spec self.send_frame(MethodFrame(self.channel_id, 60, 20, args)) if not nowait: self._pending_consumers.append((consumer, cb)) self.channel.add_synchronous_cb(self._recv_consume_ok) else: self._consumer_cb[consumer_tag] = consumer
def consume(self, queue, consumer, consumer_tag='', no_local=False, no_ack=True, exclusive=False, nowait=True, ticket=None): ''' start a queue consumer. ''' if nowait and consumer_tag == '': consumer_tag = self._generate_consumer_tag() args = Writer() args.write_short(ticket or self.default_ticket).\ write_shortstr(queue).\ write_shortstr(consumer_tag).\ write_bits( no_local, no_ack, exclusive, nowait ).\ write_table({}) # unused according to spec self.send_frame(MethodFrame(self.channel_id, 60, 20, args)) if not nowait: self.channel.add_synchronous_cb(self._recv_consume_ok) self._pending_consumers.append(consumer) else: self._consumer_cb[consumer_tag] = consumer
def _send_close(self): args = Writer() args.write_short(self.connection._close_info['reply_code']) args.write_shortstr(self.connection._close_info['reply_text'][:255]) args.write_short(self.connection._close_info['class_id']) args.write_short(self.connection._close_info['method_id']) self.send_frame(MethodFrame(self.channel_id, 10, 50, args))
def unbind(self, exchange, source, routing_key='', nowait=True, arguments={}, ticket=None, cb=None): ''' Unbind an exchange from another. ''' nowait = nowait and self.allow_nowait() and not cb args = Writer() args.write_short(ticket or self.default_ticket).\ write_shortstr(exchange).\ write_shortstr(source).\ write_shortstr(routing_key).\ write_bit(nowait).\ write_table(arguments or {}) self.send_frame(MethodFrame(self.channel_id, 40, 40, args)) if not nowait: self._unbind_cb.append(cb) self.channel.add_synchronous_cb(self._recv_unbind_ok)
def nack(self, delivery_tag, multiple=False, requeue=False): '''Send a nack to the broker.''' args = Writer() args.write_longlong(delivery_tag).\ write_bits(multiple, requeue) self.send_frame(MethodFrame(self.channel_id, 60, 120, args))
def _send_open(self): args = Writer() args.write_shortstr(self.connection._vhost) args.write_shortstr('') args.write_bit(True) # insist flag for older amqp, not used in 0.9.1 self.send_frame(MethodFrame(self.channel_id, 10, 40, args))
def declare(self, exchange, type, passive=False, durable=False, nowait=True, arguments=None, ticket=None, cb=None): """ Declare the exchange. exchange - The name of the exchange to declare type - One of """ nowait = nowait and self.allow_nowait() and not cb args = Writer() args.write_short(ticket or self.default_ticket).\ write_shortstr(exchange).\ write_shortstr(type).\ write_bits(passive, durable, False, False, nowait).\ write_table(arguments or {}) self.send_frame(MethodFrame(self.channel_id, 40, 10, args)) if not nowait: self._declare_cb.append(cb) self.channel.add_synchronous_cb(self._recv_declare_ok)
def test_field_table(self): w = Writer() expect(w.write_table).args({'foo': 'bar'}).side_effect( lambda *args: w._output_buffer.extend('tdata')) w._field_table({'foo': 'bar'}) assert_equals('Ftdata', w._output_buffer)
def test_write_short_at(self): w = Writer(bytearray('\x00' * 6)) assert_true(w is w.write_short_at(2**16 - 1, 2)) assert_equals(bytearray('\x00\x00\xff\xff\x00\x00'), w._output_buffer) assert_raises(ValueError, w.write_short_at, -1, 2) assert_raises(ValueError, w.write_short_at, 2**16, 3)
def write_frame(self, buf): ''' Write the frame into an existing buffer. ''' writer = Writer(buf) writer.write_octet(self.type()) writer.write_short(self.channel_id) # Track the position where we're going to write the total length # of the frame arguments. stream_args_len_pos = len(buf) writer.write_long(0) stream_method_pos = len(buf) writer.write_short(self._class_id) writer.write_short(self._weight) writer.write_longlong(self._size) # Like frame parsing, branch to faster code for default properties if self.DEFAULT_PROPERTIES: # Track the position where we're going to write the flags. flags_pos = len(buf) writer.write_short(0) flag_bits = 0 for key, proptype, rfunc, wfunc, mask in self.PROPERTIES: val = self._properties.get(key, None) if val is not None: flag_bits |= mask wfunc(writer, val) writer.write_short_at(flag_bits, flags_pos) else: shift = 15 flag_bits = 0 flags = [] stack = deque() for key, proptype, rfunc, wfunc, mask in self.PROPERTIES: val = self._properties.get(key, None) if val is not None: if shift == 0: flags.append(flag_bits) flag_bits = 0 shift = 15 flag_bits |= (1 << shift) stack.append((wfunc, val)) shift -= 1 flags.append(flag_bits) for flag_bits in flags: writer.write_short(flag_bits) for method, val in stack: method(writer, val) # Write the total length back at the beginning of the frame stream_len = len(buf) - stream_method_pos writer.write_long_at(stream_len, stream_args_len_pos) writer.write_octet(0xce)
def _send_flow(self, active): ''' Send a flow control command. ''' args = Writer() args.write_bit(active) self.send_frame(MethodFrame(self.channel_id, 20, 20, args)) self.channel.add_synchronous_cb(self._recv_flow_ok)
def open(self): ''' Open the channel for communication. ''' args = Writer() args.write_shortstr('') self.send_frame(MethodFrame(self.channel_id, 20, 10, args)) self.channel.add_synchronous_cb(self._recv_open_ok)
def test_write_short(self): w = Writer() assert_true(w is w.write_short(0)) assert_true(w is w.write_short(2**16 - 2)) assert_equals(bytearray('\x00\x00\xff\xfe'), w._output_buffer) assert_raises(ValueError, w.write_short, -1) assert_raises(ValueError, w.write_short, 2**16)
def test_write_octet(self): w = Writer() assert_true(w is w.write_octet(0)) assert_true(w is w.write_octet(255)) assert_equals(bytearray('\x00\xff'), w._output_buffer) assert_raises(ValueError, w.write_octet, -1) assert_raises(ValueError, w.write_octet, 2**8)
def _send_start_ok(self): '''Send the start_ok message.''' args = Writer() args.write_table(self.connection._properties) args.write_shortstr(self.connection._login_method) args.write_longstr(self.connection._login_response) args.write_shortstr(self.connection._locale) self.send_frame(MethodFrame(self.channel_id, 10, 11, args))
def test_field_int(self): w = Writer() w._field_int(-2**15) w._field_int(2**15 - 1) assert_equals('s\x80\x00s\x7f\xff', w._output_buffer) w = Writer() w._field_int(-2**31) w._field_int(2**31 - 1) assert_equals('I\x80\x00\x00\x00I\x7f\xff\xff\xff', w._output_buffer) w = Writer() w._field_int(-2**63) w._field_int(2**63 - 1) assert_equals( 'l\x80\x00\x00\x00\x00\x00\x00\x00l\x7f\xff\xff\xff\xff\xff\xff\xff', w._output_buffer)
def test_write_long(self): w = Writer() assert_true(w is w.write_long(0)) assert_true(w is w.write_long(2**32 - 2)) assert_equals(bytearray('\x00\x00\x00\x00\xff\xff\xff\xfe'), w._output_buffer) assert_raises(ValueError, w.write_long, -1) assert_raises(ValueError, w.write_long, 2**32)