amqp.protocol – AMQP Protocol Helpers

Properties

amqp.protocol.default_version
the protocol version used if no version was specified when a connection is established. When a version of the protocol is loaded by the JavaScript interpreter it registers itself with the amqp.protocol module. The most recent version registered is set as the default version.
amqp.protocol.protocols
a dictionary of registered protocol versions

Top Level Functions

amqp.protocol.deep_copy_defaults(object)

Copy’s JavaScript objects of unlimited depth

This function is used internally to properly copy defaults into options in MetaClasses. While pointers and references are not a direct JavaScript construct, under the hood JavaScript uses these concepts extensively to avoid performance issues. Simply assigning a JavaScript object to another variable does not copy it. We must walk the object’s members and clone each sub object to create an exact copy of the original.

Parameter:object – a javascript dictionary object
Return type:a deep copy of the dictionary object
amqp.protocol.MetaClass(cls_members)

Convenience construct for creating full featured classes

This is used for classes which need a constructor and default member values. Any object passed to cls_members becomes the resulting class’ prototype.

var a = MetaClass({
    _init: function(){alert('_init called')}
});

is equivalent to:

var a = function(){};
a.prototype._init = function(){alert('_init called')};

MetaClass classes have a few special members:

  • _init - This is the object constructor method. It is run when the resulting class is instantiated.
  • _new - This is the class constructor method. It is run when the class is first created.
  • _defaults - This should be a dictionary object (e.g. {}). It lists the options the _init constructor can accept and their default values if they are not passed into the constructor

A MetaClass class looks something like this:

var person = MetaClass({
    _defaults: {
        name: 'Unknown',
        id: 1
    },
    _init: function(options){
        this._options = options;
        alert(options.name + ' has id ' + options.id);
    },
    talk: function(words) {
        alert(this._options.name + ' says '  + words);
    }

});

You would use this object as such:

var john = new person({name: 'John'});
// alert | John has id 1 |
john.talk('hello!!!');
// alert | John says hello!!! |
Parameter:cls_members – object of class methods and properties
Return type:JavaScript class prototype

Misc. Module Methods

amqp.protocol.register(version, module)

When loaded a versioned protocol script will use this method to register itself

This must be called at the end of every protocol module. For instance the version 0.10 amqp module does something similar to this:

(function(){
    amqp.protocol_0_10 = {
    .
    .
    .
    };

amqp.protocol.register('0.10', amqp.protocol_0_10);
})();
Parameters:
  • version – the version of the module being registered
  • module – a reference to the module being registered
amqp.protocol.get_connection(version, options)

get an instantiated connection object from a versioned protocol library

This is the entry point for creating connections to an AMQP server. Options are version dependent but usually can contain these members:

  • host - the host name to connect to
  • port - the port number to connect to
  • username - if authentication is required, use this to set the username
  • password - if authentication is required, use this to set the password
  • send_hook - used for debugging, if set to a function this function will be called with the raw byte stream and parsed frame data as parameters every time data is sent over this connection
  • recive_hook - used for debugging, if set to a function this function will be called with the raw byte stream and parsed frame data as parameters every time data is recived over this connection

If a version is not specified in the parameters (e.g. version==null || version==undefined) the default version is assumed.

amqp_conn = amqp.protocol.get_connection(null,
                                         {host:'localhost',
                                          port:7000,
                                          send_hook: function(data, frame) {
                                              append_msg('SENT', frame, 'message_list');
                                          },
                                          recive_hook: function(data, frame) {
                                              append_msg('RECV', frame, 'message_list');
                                          }
                                         });

amqp_conn.start();
Note:

Most higher level bindings will wrap this in their own higher level connection object. This is simply a convenience function for getting a raw versioned AMQP connection. A higher level binding may want to negotiate versions with the server.

Parameters:
  • version – The version you wish to use or null for the default version
  • options – The options to use when creating teh connection

Decoder and Encoder Module Methods

Decoders and encoders are the heart of the AMQP binding stack. AMQP messages are essentially streams of tightly packed encoded bytes which we marshal into data structures and then pass off to handlers which act on the data. The amqp.protocol module contains a number of data marshalers which are used by the generated protocol code to do all the heavy lifting. We do this because data marshaling of base types, and even some complex types shouldn’t change from version to version of the protocol. If we find a bug in the marshaling code we can fix it in one place instead of a dozen. Most of this code is also generic enough to use for any type of binary encoding inside of JavaScript, not just the AMQP protocol.

Helper Methods

amqp.protocol.int_to_bytestr(u, size)

Takes a numerical value and encodes it as a bytestring in network byte order

The name of this method is a bit off since it will take any numerical value and treat it as a unsigned int when encoding it into a byte string. It is up to the decoder used to reinterpret the correct type from the byte string representation. This method is used by the other encoders to encode any numerical value they may have. It works by masking off 8 bits at a time, converting that to a character and then right shifting by 8 bits.

Parameters:
  • u – the integer to encode
  • size – the number of bytes to encode into
Return type:

a bytestring

amqp.protocol.decode_typecode(assembly)

Reads a uint of size 1 from the assembly stream and returns the value

When a field’s type in the data stream is not implicitly known, the type code will be encoded as a one byte uint. This method reads the byte and decodes it to a uint.

Parameter:assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
Return type:an unsigned integer representing the typecode we are reading from the bytestream
amqp.protocol.encode_typecode(assembly, typecode)

Writes the typecode to the assembly stream

Takes a typecode and encodes it as a 1 byte uint. If the typecode is ‘’‘’null’‘’‘, no value is encoded.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writing to
  • typecode – the unsigned integer we are writing to the stream

Static Type Decoding Methods

Static type decoders are used directly by the generated code to handle any decoding duties for types with a static size. As such every static type decoder has a single signature:

decode_<type_name> = function(assembly, size);

This makes it easy to add decoders if necessary. All decoders read in size bytes from the assembly and returns the processed value to the caller.

amqp.protocol.decode_bin(assembly, size)

This function simply ready size bytes from the assembly

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • size – the number of bytes to read from the stream
Return type:

byte string

amqp.protocol.decode_int(assembly, size)

Read in size bytes, convert to a uint by bit shifting and adding then check if the most significant bit is set. If it is take the twos compliment to convert to a negitive integer.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • size – the number of bytes to read from the stream
Return type:

signed integer

amqp.protocol.decode_uint(assembly, size)

Read in size bytes and convert to a uint by bit shifting and adding

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • size – the number of bytes to read from the stream
Return type:

unsigned integer

amqp.protocol.decode_void(assembly, size)

Special case where we encounter a void typecode but don’t read any further bytes. Always return null.

Parameters:
  • assembly – unused
  • size – size must be 0
Return type:

null

amqp.protocol.decode_bool(assembly, size)

Reads in a uint of the given size and if it is greater than 0 return true else return false.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • size – the number of bytes to read from the stream
Return type:

boolean

amqp.protocol.decode_dec(assembly, size)

Right now we just decode this as a float. We read in the first byte to find out where the decimal place goes amd then decode the rest of the bytes and devide that number by the decimal place * 10. While this gives us the intended value it does not preserve the fixed decimal and can not be directly reencoded. Since JavaScript has no way of creating a new class that works like primitive types (e.g. overloaded operators) it is hard to create our own decimal type. In the end it may just need to be special cased and users will have to be aware of the issues.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • size – the number of bytes to read from the stream
Return type:

float - this is wrong as decimal type has a fixed decimal place

amqp.protocol.decode_datetime(assembly, size)

Reads in a uint of the specified size and calls setTime on a created Date object.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • size – the number of bytes to read from the stream
Return type:

Date object

amqp.protocol.decode_float(assembly, size)

FIXME: This is currently not implemented correctly

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • size – the number of bytes to read from the stream
Return type:

n/a

amqp.protocol.decode_bit(assembly, size)

This is a special case decoder in which we always return 1. This is because this decoder is only called if the packing flag is set. Bits are read from the packing flags and not from the payload. Since the library only calls a decode function if the packing flag is set we will only ever be called if the bit is set to 1. Doing it this way avoids complicating the code by special casing when reading the packing flags.

Parameters:
  • assembly – not used
  • size – should be 0
Return type:

1

Static Type Encoding Methods

Static type encoders are used directly by the generated code to handle any encoding duties for types with a static size. As such every static type decoder has a single signature:

encode_<type_name> = function(assembly, typecode, size, value);

This makes it easy to add encoders if necessary. All encoders write out a bytestream representation of the value in the give size. If typecode is not null we also prepend the typecode to the encoded value.

amqp.protocol.encode_bin(assembly, typecode, size, value)

Takes the value and copies it verbatum into the assembly byte stream

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to
  • typecode – if not null the typecode gets prepended to the encoded value
  • size – the number of bytes to write to the stream
  • value – The byte string to encode
amqp.protocol.encode_int(assembly, typecode, size, value)

Encodes each byte by shifting and masking the last eight bytes and converting the value to an ascii character. We assume the JavaScript engine encodes in twos complement.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to
  • typecode – if not null the typecode gets prepended to the encoded value
  • size – the number of bytes to write to the stream
  • value – The integer to encode
amqp.protocol.encode_uint(assembly, typecode, size, value)

Encodes each byte by shifting and masking the last eight bytes and converting the value to an ascii character.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to
  • typecode – if not null the typecode gets prepended to the encoded value
  • size – the number of bytes to write to the stream
  • value – The unsigned integer to encode
amqp.protocol.encode_void(assembly, typecode, size, value)

Writes out the typecode and nothing else.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to
  • typecode – can not be null
  • size – must be 0
  • value – The byte string to encode
encode_bool(assembly, typecode, size, value

If true we encode a uint of 1 else a uint of 0.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to
  • typecode – if not null the typecode gets prepended to the encoded value
  • size – the number of bytes to write to the stream
  • value – must be true or false
encode_dec(assembly, typecode, size, value

Not implemented yet

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to
  • typecode – if not null the typecode gets prepended to the encoded value
  • size – the number of bytes to write to the stream
  • value – The decimal number to encode
encode_datetime(assembly,typecode, size, value

Get the number of seconds from the epoc using value.getTime() and encode it as a uint.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to
  • typecode – if not null the typecode gets prepended to the encoded value
  • size – the number of bytes to write to the stream
  • value – The Date object to encode
encode_float(assembly, typecode, size, value

Not implemented!

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to
  • typecode – if not null the typecode gets prepended to the encoded value
  • size – the number of bytes to write to the stream
  • value – The float to encode
amqp.protocol.encode_bit(assembly, typecode, size, value)

A noop since this is special cased inside the versioned protocol code.

Parameters:
  • assembly – not used
  • typecode – not used
  • size – not used
  • value – not used

Variable Type Decoding Methods

Variable type decoders are used directly by the generated code to handle any decoding duties for types with a dynamic size (e.g. their size is encoded along with the value). As such every variable type decoder has a single signature:

decode_<type_name> = function(assembly, varsize);

This makes it easy to add decoders if necessary. All decoders read in varsize bytes from the assembly to first get the size of the variable data. The decoder then reads in size bytes and returns the processed value to the caller.

amqp.protocol.decode_map(assembly, varsize)

To decode a map we read in varsize bytes to get the size of the map data We then read in 4 bytes to get the number of elements in the map. For each element we read in a string as the key, a typecode for the value and the value itself.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • varsize – the number of bytes to read in order to get the actual size of the data
Return type:

object

amqp.protocol.decode_array(assembly, varsize)

To decode an array we read in varsize bytes to get the size of the array data We then read in the typecode to get the type for the elements in the array. We then read in 4 bytes to get the element count and then we read in each element based on the typecode.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • varsize – the number of bytes to read in order to get the actual size of the data
Return type:

array

amqp.protocol.decode_str(assembly, varsize)

To decode a string we read in varsize bytes to get the size of the string data. We then read in size bytes and return the results.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • varsize – the number of bytes to read in order to get the actual size of the data
Return type:

string

amqp.protocol.decode_seq_set(assembly, varsize)

To decode a sequence set we read in varsize bytes to get the size of the set. Each byte in the set gets decoded as a unsigned integer. The lower bound of the sequence is taken from the first 4 significant bits of the decoded value. The upper bount is taken from the last 4 significant bits. These are placed in a tuple as [lower, upper] and then pushed into a list.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
  • varsize – the number of bytes to read in order to get the actual size of the data
Return type:

a list of two element tupples [[upper, lower],...]

Variable Type Encoding Methods

Variable type encoders are used directly by the generated code to handle any encoding duties for types with a dynamic size (e.g. their size is encoded along with the value). As such every variable type decoder has a single signature:

decode_<type_name> = function(assembly, typecode, size, value);

This makes it easy to add encoders if necessary. All encoders write out their data to a seperate assembly to first get the size of the variable data. They then write out the size and the encoded data to passed in assembly.

amqp.protocol.encode_map(assembly, typecode, size, value)

To encode a map we first loop over all the key/value pairs in an object. For each element we encode the key as a string and the value along with its type code. We then take the size of the resulting encoding and write the whole thing out to the main assembly.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to
  • typecode – if not null the typecode gets prepended to the encoded value
  • size – the number of bytes to encode the variable size of the data into
  • value – a JavaScript object
amqp.protocol.encode_array(assembly, typecode, size, value)
Not implemented yet
amqp.protocol.encode_str(assembly, typecode, size, value)

Get the length of the string. Encoded the length and then the string.

Parameters:
  • assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to
  • typecode – if not null the typecode gets prepended to the encoded value
  • size – the number of bytes to encode the variable size of the data into
  • value – a string
amqp.protocol.encode_seq_set(assembly, typecode, size, value)
Not implemented yet

Higher Level Encoders and Decoders

Various convinience marshallers.

amqp.protocol.decode_header(assembly)

Decodes the AMQP header and gets the class, instance and version infromation from the stream. This can be used to determine which plugin to load.

Parameter:assemblyamqp.protocol.Assembly object which contains the bytestream we are reading from
Return type:{class: int, instance: int, version_major: int, version_minor: int}
amqp.protocol.encode_header(assembly)

Gets the header from the current call context and writes it out.

Parameter:assemblyamqp.protocol.Assembly object which contains the bytestream we are writting to

Classes

class amqp.protocol.Assembly({data:''})

This class makes it easy to read and write sequential bits and bytes while avoiding unnecessary copying. When data comes over a socket it is placed in an amqp.protocol.Assembly object and passed to the parsers.

param data:Use this to set the initial stream data
read_bit(count)

Reads count bits of the current byte in the stream. This is used to read fields smaller than a byte. Fields can not cross the byte boundry, so trying to read more bits than are in the current byte will cause an error to be thrown.

Parameter:count – The number of bits to be read
read_byte(count)

Reads count bytes from the byte stream and increments the seek position. The stream must be byte aligned (e.g. if read_bit was previouly called, it must have finished reading the whole byte)

Parameter:count – The number of bytes to be read
write(bytes)

Append bytes to the data stream.

Parameter:bytes – the data to append
prepend(bytes)

Prepends bytes to the data stream

Parameter:bytes – the data to prepend
get_data()

Returns the raw data stram.

Return type:string
get_size(from_current_pos)

Returns the size of the data stream. If from_current_pos is set to true return the size of the data stream from the current seek position.

Parameter:from_current_pos – specifies if you want the total size of the stream or the size from the current seek position.
Return type:integer
seek(pos)

Sets the current byte position.

Parameter:pos – the position in the byte stream to set the read cursor to
eof()

Returns true if we are currently at the end of the stream.

Return type:boolean
mark_start()
Used for debugging this method allows us to split a stream up without having to actually split the stream which would involve large copying of data. When called the current seek position is stored.
get_start_pos()

Used for debugging this returns the position stored when mark_start was last called.

Return type:integer
get_pos()

Returns the current seek position.

Return type:integer
Message({parsed_data:{},
template: null
})

This is a wrapper class used by the generated bindings when encoding an AMQP message from a message template.

NOTE: Message is currently only used for encoding though it could be used for decoding messages also with a few tweaks.

Parameters:
  • parsed_data – Use this to set the values to encode to an Assembly
  • template – use this to specifiy which generated message template will be used for encoding
get(key)

Convinience method for grabbing data from the parsed_data map.

Parameter:key – the id of the value we wish to grab
Return type:the value inside of the parsed_data map pointed to by key
set(key, value)

Convinience method for setting data inside the parsed_data map.

Parameters:
  • key – the id of the value we wish to set
  • value – the data to place into the map
get_size()

Get’s the size of the internal amqp.protocol.Assembly from the current seek position.

Return type:integer
get_data(size)

Reads size bytes from the internal assembly.

Parameter:size – The number of bytes to read
Return type:string of bytes
get_type()

Returns the type code of message we are encoding

Return type:integer
get_name()

Returns the class name of the message we are encoding

Return type:string
get_message_name()

Returns the message name of the message we are encoding

Return type:string
encode()
Using the template and parsed_data, encodes the message into the internal amqp.protocol.Assembly
decode()
Not used anywhere but this will decode the contents of the internal amqp.protocol.Assembly.