This section describes the wire-format Agnos uses to encode values, and serves as a reference for new implementations of libagnos (for other languages). Note that the java implementation of libagnos is the “reference implementation”. Refer to Data Types for more information on types.
Note
Some notes of notations:
The protocol is very simple, and is made of messages. Each message consists of a header, followed by the payload.
Order | Description | Size (bytes) |
---|---|---|
1 | Sequence number | 4 (int32) |
2 | Message length (in bytes) | 4 (int32) |
3 | Uncompressed length (in bytes) | 4 (int32) |
If the uncompressed length is 0, it means the message has not been compressed and thus no decompression should take place – the “on-the-wire” length (second integer in the header) is also the actual length. In this case, you can continue processing the payload as-is.
If this field is greater than 0, the message has been compressed. The “on-the-wire” length denotes the compressed size (number of bytes over-the-wire), while the uncompressed length denotes the original size of the message, before compression. In this case, you have to decompress the payload, prior to continuing its processing.
Agnos uses the free and widely-available DEFLATE algorithm for compression (as implemented by zlib).
If the message is sent by the client (a request), the first byte is the command code (see below), followed by the command’s payload.
If the message is sent by the server (a reply), the first byte is the reply code (see below), followed by the reply’s payload.
Command | Value |
---|---|
CMD_PING | 0 |
CMD_INVOKE | 1 |
CMD_QUIT | 2 |
CMD_DECREF | 3 |
CMD_INCREF | 4 |
CMD_GETINFO | 5 |
CMD_CHECK_CAST | 6 |
CMD_QUERY_PROXY_TYPE | 7 |
Reply | Value |
---|---|
REPLY_SUCCESS | 0 |
REPLY_PROTOCOL_ERROR | 1 |
REPLY_PACKED_EXCEPTION | 2 |
REPLY_GENERIC_EXCEPTION | 3 |
These codes are used when a client calls getServiceInfo():
Query | Value | Description |
---|---|---|
INFO_META | 0 | Queries meta information, i.e., the INFO_XXX codes that the server supports |
INFO_SERVICE | 1 | Queries information about the service (name, version, IDL digest, etc) |
INFO_FUNCTIONS | 2 | Queries information about the functions exposed by the service (function names, argument types and return types) |
INFO_REFLECTION | 3 | Queries reflection information about the service, which includes pretty much everything found in the IDL file (classes, constants, enums, records, and functions) |
Size: 4+
Packer ID: Varies for every T. The following types have predefined IDs:
Type |
ID |
---|---|
list[int8] |
800 |
list[bool] |
801 |
list[int16] |
802 |
list[int32] |
803 |
list[int64] |
804 |
list[float] |
805 |
list[buffer] |
806 |
list[date] |
807 |
list[str] |
808 |
Layout: 4 bytes length specifier (in int32 format), followed by that many instances of T.
Examples:
Size: 4+
Packer ID: Varies for every T. The following types have predefined IDs:
Type |
ID |
---|---|
set[int8] |
820 |
set[bool] |
821 |
set[int16] |
822 |
set[int32] |
823 |
set[int64] |
824 |
set[float] |
825 |
set[buffer] |
826 |
set[date] |
827 |
set[str] |
828 |
Layout: 4 bytes length specifier (in int32 format), followed by that many instances of T.
Examples:
Size: 4+
Packer ID: Varies for every K and V. The following types have predefined IDs:
Type |
ID |
---|---|
map[int32, int32] |
850 |
map[int32, str] |
851 |
map[str, int32] |
852 |
map[str, str] |
853 |
Layout: 4 bytes length specifier (in int32 format), followed by that many instances of K-and-V pairs.
Examples:
Size: 4+
Packer ID:
Layout: 4 bytes length specifier (in int32 format), followed by that many instances of (key-packer-id, key, value-packer-id, value): each item is a key-value pair that also stores a key-packer and a value-packer (denoted by their packer IDs as int32).
Example: [00 00 00 02 . 00 00 00 09 . 00 00 00 04 . 6E 61 6D 65 . 00 00 00 09 . 00 00 00 04 . 4A 6f 68 6E . 00 00 00 09 . 00 00 00 03 . 61 67 65 . 00 00 00 04 . 00 00 00 2A]. For convenience, here’s the data structure parsed:
00 00 00 02 # number of items
# first item
00 00 00 09 # key packer ID (String)
00 00 00 04 . 6E 61 6D 65 # key
00 00 00 09 # value packer id (String)
00 00 00 04 . 4A 6f 68 6E # value
# second item
00 00 00 09 # key packer ID (String)
00 00 00 03 . 61 67 65 # key
00 00 00 04 # value packer ID (Int32)
00 00 00 2A # value
which encodes
HeteroMap h = new HeteroMap();
h.put("name", "John");
h.put("age", 42);
In this section we’ll examine a “captured” session between an Agnos client and an Agnos server. The session was generated by capturing the transport of the python unit-test.
In this part, the client attempts to invoke a remote function, whose signature is
PersonProxy createPerson(String name, PersonProxy father, PersonProxy mother);
The code the client executes is
PersonProxy eve = createPerson("eve", null, null);
Client sends: [00 00 00 04 . 00 00 00 1c . 00 00 00 00 . 01 . 00 0d bb cb . 00 00 00 03 . 65 76 65 . ff ff ff ff ff ff ff ff . ff ff ff ff ff ff ff ff]
Header:
Payload:
Following are the function’s arguments:
Server sends in response: [00 00 00 04 . 00 00 00 09 . 00 00 00 00 . 00 . 00 00 00 00 09 7a 85 8c]
Header:
Payload:
The client created a second person (adam), and now it attempts to marry the two, with the following code
eve.marry(adam);
Client sends: [00 00 00 06 . 00 00 00 15 . 00 00 00 00 . 01 . 00 0d bc 32 . 00 00 00 00 09 7a 85 8c . 00 00 00 00 09 7a 86 6c]
The sequence number is 6, followed by the message length (21), and no compression is used (0). The command code is CMD_INVOKE, followed by the function ID (900146), and it’s arguments: [00 00 00 00 09 7a 85 8c] and [00 00 00 00 09 7a 86 6c].
The first argument is the this or self instance of the method marry(), and in our case, it’s eve‘s reference number. The second argument is adam‘s reference number.
Server sends: [00 00 00 06 . 00 00 00 01 . 00 00 00 00 . 00]
The sequence number is 6, followed by the message length (1), and no compression is used. The reply code is REPLY_SUCCESS, and this is it – the method’s return type is void.
Now the client attempts to invoke
adam.marry(eve);
But adam is is already married. This will result in an exception.
Client sends: [00 00 00 09 . 00 00 00 15 . 00 00 00 00 . 01 . 00 0d bc 32 . 00 00 00 00 09 7a 86 6c . 00 00 00 00 09 7a 85 8c]
The service implementation throws an exception, as it does not allow for persons to be re-married.
Server sends: [00 00 00 09 . 00 00 00 20 . 00 00 00 00 . 02 . 00 0d bb ae . 00 00 00 0f . 61 6c 72 65 61 64 79 20 6d 61 72 72 69 65 64 . 00 00 00 00 09 7a 86 6c]
The sequence number is 9, followed by the message length (32), and no compression is used. The reply code is REPLY_PACKED_EXCEPTION, which indicates that a packed-exception follows. The exception class ID is 900014 (MartialStatusError in this case), whose constructor takes String message (a description of the error) and PersonProxy person (indicates the person who’s already married).
So here the exception message is "already married", and the “offending” person is adam (it his reference number that’s returned).