sergeych edited this page Oct 23, 2018 · 6 revisions

Protocol description

Farcall sends and receives data structures, which we will format in {json: 'notation'}. The format of the data package that conveys our structures, which we will call packets, does not matter. This gem, for example, supports JSON and BOSS, and you can easily implement it with XML, YAML and whatever else.

The protocol is symmetric: each party can issue commands and answer on them with some return data or error report.

Each packet hash a serial number that starts from 0 and counts up on each side: {serial: 0,...}. Each side counts own sent packets. If a party receives a packet with serial that is not exactly equal to last received serial or 0, the connection is considered lost.

There are only two types of packets: one to issue remote call request, and another to provide result of the remote command invocation. Fairly simple.

Command packet

Is used to request execution of a named command on the remote party

{ serial: sn, cmd: 'command_name', args: [], kwargs: {} }

Command name could be any string.

args is the the array of arguments, the only type that languages like C, Java and like supports.

kwargs are keyword arguments, which are happily used in Ruby, Python and other human languages. Farcall let grow both. It is recommended that in the machine languages which have no kwargs, pass it as the only or the last argument to method call.

Each command can use no args or any combination.

Return packet

Is always issued by the remote party when it has received and performed command execution. Return packet has two variation: success and error, but each one must contain ref field that has the serial number of the corresponding command packet.

If the command is successfully executed:

{serial: sn, ref: command_serial_number, result: any_object }

result can be any object that command returned: array, structure, string, float, whatever, or null. If you need no result, pass result: null or omit result field.

If some error has happened, say, the command is not known, or threw an exception while executing, the packed should be:

{ serial: sn, 
  ref: command_serial_number, 
  error: { 
    class: 'class_name', 
    text: 'explanation' 
  } 
}

Note, that error packet can not have result key. error.class is a string that somehow descrive the class of the error, like exception class name. error.text is used to distimguish errors in the same class. You can pass more error fields, but these 2 are mandatory.

Farcall gem, for example, converts any exception rescued while executing remote commfnd into exception Farcall::RemoteError which holds text and class, and raises it on the calling side. If the command does not exist in the provider instance, Ruby raises exception that is conveyed the same way.

Note that if the calling party does not need to wait for the command to execute and check the result, it can simply ignore return packets, but the callee should always issue it.

sample protocol

Direction is coded with first column: '>' means that party 1 (p1) issues the packet, '<' that party 2 (p1) sends.

> { serial: 0, cmd: 'hello', args: ['world'], kwargs: {} }  # p1 calls hello('world')
> { serial: 1, cmd: 'foo', args: [], kwargs: {bar: 'baz'} } # p1 calls foo(bar: 'baz')
< { serial: 0, cmd: 'who', args:[], kwargs: {} }            # p2 calls who()
> { serial: 2, ref: 0, result: 'friend' }                   # p1 answers who() -> 'friend'
< { serial: 1, ref: 1 }                                     # p2 executed foo(bar: baz) first
< { serial: 2, ref: 0, error: 
     { class: 'AuthentincationRequired', 
       text: 'I don not know you'
     }
  }                                                         # p2 answers to hello() with and error

Thats all. Easy, isn't it?

what next?

Now you know how to write Farcall protocol for your favorite language and platform, why shouldn't you give yourself some fun of implementing it? We use this protocol in dozens places and feel happy with it ;) Do it and send me a link - I'll add it to the page!

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.